/*
The MIT License (MIT)

Copyright (c) 2013-2020 Winlin

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <srs_utest_protocol.hpp>

using namespace std;

#include <srs_kernel_error.hpp>
#include <srs_core_autofree.hpp>
#include <srs_protocol_utility.hpp>
#include <srs_rtmp_msg_array.hpp>
#include <srs_rtmp_stack.hpp>
#include <srs_kernel_utility.hpp>
#include <srs_app_st.hpp>
#include <srs_protocol_amf0.hpp>
#include <srs_rtmp_stack.hpp>
#include <srs_service_http_conn.hpp>

MockEmptyIO::MockEmptyIO()
{
}

MockEmptyIO::~MockEmptyIO()
{
}

srs_error_t MockEmptyIO::read_fully(void* /*buf*/, size_t /*size*/, ssize_t* /*nread*/)
{
    return srs_success;
}

srs_error_t MockEmptyIO::write(void* /*buf*/, size_t /*size*/, ssize_t* /*nwrite*/)
{
    return srs_success;
}

void MockEmptyIO::set_recv_timeout(srs_utime_t /*tm*/)
{
}

srs_utime_t MockEmptyIO::get_recv_timeout()
{
    return -1;
}

int64_t MockEmptyIO::get_recv_bytes()
{
    return -1;
}

void MockEmptyIO::set_send_timeout(srs_utime_t /*tm*/)
{
}

srs_utime_t MockEmptyIO::get_send_timeout()
{
    return 0;
}

int64_t MockEmptyIO::get_send_bytes()
{
    return 0;
}

srs_error_t MockEmptyIO::writev(const iovec */*iov*/, int /*iov_size*/, ssize_t* /*nwrite*/)
{
    return srs_success;
}

srs_error_t MockEmptyIO::read(void* /*buf*/, size_t /*size*/, ssize_t* /*nread*/)
{
    return srs_success;
}

MockBufferIO::MockBufferIO()
{
    rtm = stm = SRS_UTIME_NO_TIMEOUT;
    rbytes = sbytes = 0;
    in_err = out_err = srs_success;
}

MockBufferIO::~MockBufferIO()
{
}

int MockBufferIO::length()
{
    return in_buffer.length();
}

MockBufferIO* MockBufferIO::append(string data)
{
    in_buffer.append((char*)data.data(), data.length());
    return this;
}

MockBufferIO* MockBufferIO::append(MockBufferIO* data)
{
    in_buffer.append(&data->in_buffer);
    return this;
}

MockBufferIO* MockBufferIO::append(uint8_t* data, int size)
{
    in_buffer.append((char*)data, size);
    return this;
}

int MockBufferIO::out_length()
{
    return out_buffer.length();
}

MockBufferIO* MockBufferIO::out_append(string data)
{
    out_buffer.append((char*)data.data(), data.length());
    return this;
}

MockBufferIO* MockBufferIO::out_append(MockBufferIO* data)
{
    out_buffer.append(&data->out_buffer);
    return this;
}

MockBufferIO* MockBufferIO::out_append(uint8_t* data, int size)
{
    out_buffer.append((char*)data, size);
    return this;
}

srs_error_t MockBufferIO::read_fully(void* buf, size_t size, ssize_t* nread)
{
    if (in_err != srs_success) {
        return srs_error_copy(in_err);
    }

    if (in_buffer.length() < (int)size) {
        return srs_error_new(ERROR_SOCKET_READ, "read");
    }
    memcpy(buf, in_buffer.bytes(), size);
    
    rbytes += size;
    if (nread) {
        *nread = size;
    }
    in_buffer.erase(size);
    return srs_success;
}

srs_error_t MockBufferIO::write(void* buf, size_t size, ssize_t* nwrite)
{
    if (out_err != srs_success) {
        return srs_error_copy(out_err);
    }

    sbytes += size;
    if (nwrite) {
        *nwrite = size;
    }
    out_buffer.append((char*)buf, size);
    return srs_success;
}

void MockBufferIO::set_recv_timeout(srs_utime_t tm)
{
    rtm = tm;
}

srs_utime_t MockBufferIO::get_recv_timeout()
{
    return rtm;
}

int64_t MockBufferIO::get_recv_bytes()
{
    return rbytes;
}

void MockBufferIO::set_send_timeout(srs_utime_t tm)
{
    stm = tm;
}

srs_utime_t MockBufferIO::get_send_timeout()
{
    return stm;
}

int64_t MockBufferIO::get_send_bytes()
{
    return sbytes;
}

srs_error_t MockBufferIO::writev(const iovec *iov, int iov_size, ssize_t* nwrite)
{
    srs_error_t err = srs_success;

    if (out_err != srs_success) {
        return srs_error_copy(out_err);
    }
    
    ssize_t total = 0;
    for (int i = 0; i <iov_size; i++) {
        const iovec& pi = iov[i];
        
        ssize_t writen = 0;
        if ((err = write(pi.iov_base, pi.iov_len, &writen)) != srs_success) {
            return err;
        }
        total += writen;
    }
    
    if (nwrite) {
        *nwrite = total;
    }
    return err;
}

srs_error_t MockBufferIO::read(void* buf, size_t size, ssize_t* nread)
{
    if (in_err != srs_success) {
        return srs_error_copy(in_err);
    }

    if (in_buffer.length() <= 0) {
        return srs_error_new(ERROR_SOCKET_READ, "read");
    }
    
    size_t available = srs_min(in_buffer.length(), (int)size);
    memcpy(buf, in_buffer.bytes(), available);
    
    rbytes += available;
    if (nread) {
        *nread = available;
    }
    in_buffer.erase(available);

    return srs_success;
}

MockStatistic::MockStatistic()
{
    in = out = 0;
}

MockStatistic::~MockStatistic()
{
}

int64_t MockStatistic::get_recv_bytes()
{
    return in;
}

int64_t MockStatistic::get_send_bytes()
{
    return out;
}

MockStatistic* MockStatistic::set_in(int64_t v)
{
    in = v;
    return this;
}

MockStatistic* MockStatistic::set_out(int64_t v)
{
    out = v;
    return this;
}

MockStatistic* MockStatistic::add_in(int64_t v)
{
    in += v;
    return this;
}

MockStatistic* MockStatistic::add_out(int64_t v)
{
    out += v;
    return this;
}

MockWallClock::MockWallClock()
{
    clock = 0;
}

MockWallClock::~MockWallClock()
{
}

srs_utime_t MockWallClock::now()
{
    return clock;
}

MockWallClock* MockWallClock::set_clock(srs_utime_t v)
{
    clock = v;
    return this;
}

// verify the sha256
VOID TEST(ProtocolHandshakeTest, OpensslSha256)
{
    // randome bytes to ensure the openssl sha256 is ok.
    uint8_t random_bytes[] = {
        0x8b, 0x1c, 0x5c, 0x5c, 0x3b, 0x98, 0x60, 0x80, 0x3c, 0x97, 0x43, 0x79, 0x9c, 0x94, 0xec, 0x63, 0xaa, 0xd9, 0x10, 0xd7, 0x0d, 0x91, 0xfb, 0x1f, 0xbf, 0xe0, 0x29, 0xde, 0x77, 0x09, 0x21, 0x34, 0xa5, 0x7d, 0xdf, 0xe3, 0xdf, 0x11, 0xdf, 0xd4, 0x00, 0x57, 0x38, 0x5b, 0xae, 0x9e, 0x89, 0x35, 0xcf, 0x07, 0x48, 0xca, 0xc8, 0x25, 0x46, 0x3c,
        0xb6, 0xdb, 0x9b, 0x39, 0xa6, 0x07, 0x3d, 0xaf, 0x8b, 0x85, 0xa2, 0x2f, 0x03, 0x64, 0x5e, 0xbd, 0xb4, 0x20, 0x01, 0x48, 0x2e, 0xc2, 0xe6, 0xcc, 0xce, 0x61, 0x59, 0x47, 0xf9, 0xdd, 0xc2, 0xa2, 0xfe, 0x64, 0xe6, 0x0b, 0x41, 0x4f, 0xe4, 0x8a, 0xca, 0xbe, 0x4d, 0x0e, 0x73, 0xba, 0x82, 0x30, 0x3c, 0x53, 0x36, 0x2e, 0xd3, 0x04, 0xae, 0x49,
        0x44, 0x71, 0x6d, 0x4d, 0x5a, 0x14, 0x94, 0x94, 0x57, 0x78, 0xb9, 0x2a, 0x34, 0x49, 0xf8, 0xc2, 0xec, 0x4e, 0x29, 0xb6, 0x28, 0x54, 0x4a, 0x5e, 0x68, 0x06, 0xfe, 0xfc, 0xd5, 0x01, 0x35, 0x0c, 0x95, 0x6f, 0xe9, 0x77, 0x8a, 0xfc, 0x11, 0x15, 0x1a, 0xda, 0x6c, 0xf5, 0xba, 0x9e, 0x41, 0xd9, 0x7e, 0x0f, 0xdb, 0x33, 0xda, 0x35, 0x9d, 0x34,
        0x67, 0x8f, 0xdf, 0x71, 0x63, 0x04, 0x9c, 0x54, 0xb6, 0x18, 0x10, 0x2d, 0x42, 0xd2, 0xf3, 0x14, 0x34, 0xa1, 0x31, 0x90, 0x48, 0xc9, 0x4b, 0x87, 0xb5, 0xcd, 0x62, 0x6b, 0x77, 0x18, 0x36, 0xd9, 0xc9, 0xc9, 0xae, 0x89, 0xfb, 0xed, 0xcd, 0xcb, 0xdb, 0x6e, 0xe3, 0x22, 0xbf, 0x7b, 0x72, 0x8a, 0xc3, 0x79, 0xd6, 0x1b, 0x6c, 0xe7, 0x9c, 0xc9,
        0xfd, 0x48, 0xaa, 0xc1, 0xfa, 0xbf, 0x33, 0x87, 0x5c, 0x0d, 0xe5, 0x34, 0x24, 0x70, 0x14, 0x1e, 0x4a, 0x48, 0x07, 0x6e, 0xaf, 0xbf, 0xfe, 0x34, 0x1e, 0x1e, 0x19, 0xfc, 0xb5, 0x8a, 0x4f, 0x3c, 0xb4, 0xcf, 0xde, 0x24, 0x79, 0x65, 0x17, 0x22, 0x3f, 0xc0, 0x06, 0x76, 0x4e, 0x3c, 0xfb, 0xc3, 0xd0, 0x7f, 0x7b, 0x87, 0x5c, 0xeb, 0x97, 0x87,
    };
    
    char digest[SRS_OpensslHashSize];
    ASSERT_EQ(ERROR_SUCCESS, 
        openssl_HMACsha256(
            SrsGenuineFPKey, 30, 
            (char*)random_bytes, sizeof(random_bytes),
            digest
        )
    );
    
    uint8_t expect_digest[] = {
        0x1b, 0xc7, 0xe6, 0x14, 0xd5, 0x19, 0x8d, 0x99, 0x42, 0x0a, 0x21, 0x95, 0x26, 0x9a, 0x8a, 0x56,
        0xb4, 0x82, 0x2a, 0x7f, 0xd3, 0x1d, 0xc3, 0xd8, 0x92, 0x97, 0xc4, 0x61, 0xb7, 0x4d, 0x5d, 0xd2
    };
    EXPECT_TRUE(srs_bytes_equals(digest, (char*)expect_digest, 32));
}

// verify the dh key
VOID TEST(ProtocolHandshakeTest, DHKey)
{
    srs_internal::SrsDH dh;
    
    ASSERT_TRUE(ERROR_SUCCESS == dh.initialize(true));
    
    char pub_key1[128];
    int pkey_size = 128;
    EXPECT_TRUE(ERROR_SUCCESS == dh.copy_public_key(pub_key1, pkey_size));
    ASSERT_EQ(128, pkey_size);
    
    char pub_key2[128];
    EXPECT_TRUE(ERROR_SUCCESS == dh.copy_public_key(pub_key2, pkey_size));
    ASSERT_EQ(128, pkey_size);
    
    EXPECT_TRUE(srs_bytes_equals(pub_key1, pub_key2, 128));
    
    // another dh
    srs_internal::SrsDH dh0;
    
    ASSERT_TRUE(ERROR_SUCCESS == dh0.initialize(true));
    
    EXPECT_TRUE(ERROR_SUCCESS == dh0.copy_public_key(pub_key2, pkey_size));
    ASSERT_EQ(128, pkey_size);
    
    EXPECT_FALSE(srs_bytes_equals(pub_key1, pub_key2, 128));
}

// flash will sendout a c0c1 encrypt by ssl.
VOID TEST(ProtocolHandshakeTest, VerifyFPC0C1)
{
    uint8_t c0c1[] = {
        0x03, 0x00, 0x0f, 0x64, 0xd0, 0x80, 0x00, 0x07, 0x02, 0xe6, 0x42, 0xe5, 0x2b, 0xf1, 0x1d, 0x0f, 0x6c, 0xc8, 0x50, 0xf2, 0x06, 0xae, 0xd5, 0x4f, 0xdb, 0xfe, 0x79, 0xc2, 0xef, 0xf5, 0x01, 0x74, 0x4b, 0x5b, 0xe7, 0x37, 0xa3, 0xe0, 0xca, 0xe1, 0x97, 0x07, 0xdb, 0x54, 0x1d, 0x4c, 0x4b, 0xa3, 0xc3, 0x3e, 0xa9, 0xeb, 0xa9, 0x5b, 0x2f, 0x38, 0xa0, 0xa9, 0x98, 0x38, 0x80, 0x1b, 0xfb, 0xa7, 0x04, 0xff, 0xfd, 0x45, 0xfe, 0xfa, 0xc1, 0xe4, 0x1c, 0x77, 0x9a, 0x19, 0x39, 0x34, 0x10, 0x79, 0x12, 0xcf, 0x4e, 0xea, 0x34, 0x7d, 0x88, 0x47, 0xca, 0xf2, 0xb3, 0x09, 0x50, 0xbb, 0xe1, 0x20, 0x9b, 0x25, 0xb0, 0x3c, 0xbc, 0x46, 0x7a, 0x36, 0xb8, 0xc2, 0x4d, 0xd0, 0xf1, 0x20, 0x2a, 0xcc, 0x7a, 0x91, 0xab, 0x0b, 0xb6, 0xc7, 0x09, 0x0d, 0xf1, 0x34, 0x0c, 0x37, 0xbe, 0xad, 0x0e, 0xe3, 0x6b, 0x68, 0x0a, 0x7e, 0xd2, 0xd4, 0xc5, 0x3d, 0xdc, 0xac, 0x28, 0x8b, 0x88, 0xb5, 0x1e, 0xd8, 0x2b, 0x68, 0x72, 0x55, 0x64, 0xa2, 0xa5, 0x69, 0x0a, 0xdb, 0x26, 0xff, 0x63, 0x2d, 0xb8, 0xff, 0xb6, 0x33, 0xd3, 0x9d, 0x5c, 0x46, 0xd6, 0xbf, 0x8b, 0x1c, 0x5c, 0x5c, 0x3b, 0x98, 0x60, 0x80, 0x3c, 0x97, 0x43, 0x79, 0x9c, 0x94, 0xec, 0x63, 0xaa, 0xd9, 0x10, 0xd7, 0x0d, 0x91, 0xfb, 0x1f, 0xbf, 0xe0, 0x29, 0xde, 0x77, 0x09, 0x21, 0x34, 0xa5, 0x7d, 0xdf, 0xe3, 0xdf, 0x11, 0xdf, 0xd4, 0x00, 0x57, 0x38, 0x5b, 0xae, 0x9e, 0x89, 0x35, 0xcf, 0x07, 0x48, 0xca, 0xc8, 0x25, 0x46, 0x3c,
        0xb6, 0xdb, 0x9b, 0x39, 0xa6, 0x07, 0x3d, 0xaf, 0x8b, 0x85, 0xa2, 0x2f, 0x03, 0x64, 0x5e, 0xbd, 0xb4, 0x20, 0x01, 0x48, 0x2e, 0xc2, 0xe6, 0xcc, 0xce, 0x61, 0x59, 0x47, 0xf9, 0xdd, 0xc2, 0xa2, 0xfe, 0x64, 0xe6, 0x0b, 0x41, 0x4f, 0xe4, 0x8a, 0xca, 0xbe, 0x4d, 0x0e, 0x73, 0xba, 0x82, 0x30, 0x3c, 0x53, 0x36, 0x2e, 0xd3, 0x04, 0xae, 0x49, 0x44, 0x71, 0x6d, 0x4d, 0x5a, 0x14, 0x94, 0x94, 0x57, 0x78, 0xb9, 0x2a, 0x34, 0x49, 0xf8, 0xc2, 0xec, 0x4e, 0x29, 0xb6, 0x28, 0x54, 0x4a, 0x5e, 0x68, 0x06, 0xfe, 0xfc, 0xd5, 0x01, 0x35, 0x0c, 0x95, 0x6f, 0xe9, 0x77, 0x8a, 0xfc, 0x11, 0x15, 0x1a, 0xda, 0x6c, 0xf5, 0xba, 0x9e, 0x41, 0xd9, 0x7e, 0x0f, 0xdb, 0x33, 0xda, 0x35, 0x9d, 0x34, 0x67, 0x8f, 0xdf, 0x71, 0x63, 0x04, 0x9c, 0x54, 0xb6, 0x18, 0x10, 0x2d, 0x42, 0xd2, 0xf3, 0x14, 0x34, 0xa1, 0x31, 0x90, 0x48, 0xc9, 0x4b, 0x87, 0xb5, 0xcd, 0x62, 0x6b, 0x77, 0x18, 0x36, 0xd9, 0xc9, 0xc9, 0xae, 0x89, 0xfb, 0xed, 0xcd, 0xcb, 0xdb, 0x6e, 0xe3, 0x22, 0xbf, 0x7b, 0x72, 0x8a, 0xc3, 0x79, 0xd6, 0x1b, 0x6c, 0xe7, 0x9c, 0xc9, 0xfd, 0x48, 0xaa, 0xc1, 0xfa, 0xbf, 0x33, 0x87, 0x5c, 0x0d, 0xe5, 0x34, 0x24, 0x70, 0x14, 0x1e, 0x4a, 0x48, 0x07, 0x6e, 0xaf, 0xbf, 0xfe, 0x34, 0x1e, 0x1e, 0x19, 0xfc, 0xb5, 0x8a, 0x4f, 0x3c, 0xb4, 0xcf, 0xde, 0x24, 0x79, 0x65, 0x17, 0x22, 0x3f, 0xc0, 0x06, 0x76, 0x4e, 0x3c, 0xfb, 0xc3, 0xd0, 0x7f, 0x7b, 0x87, 0x5c, 0xeb, 0x97, 0x87,
        0x99, 0x20, 0x70, 0x7b, 0xf8, 0x97, 0x73, 0xdc, 0xb4, 0x94, 0x43, 0x27, 0x03, 0xbd, 0xb5, 0x91, 0xd9, 0x3e, 0x51, 0x1a, 0xd5, 0x60, 0x9c, 0x71, 0xd3, 0xc7, 0x1f, 0xd7, 0xef, 0x2f, 0xa1, 0xf7, 0xe6, 0xb1, 0x31, 0x9d, 0xec, 0xa3, 0xe1, 0x01, 0x57, 0xa8, 0x1c, 0x34, 0xf8, 0x82, 0xf5, 0x4d, 0xb8, 0x32, 0xe4, 0x4b, 0x90, 0x97, 0xcf, 0x8c, 0x2e, 0x89, 0xd0, 0xbc, 0xc0, 0xca, 0x45, 0x5e, 0x5c, 0x36, 0x47, 0x98, 0xa8, 0x57, 0xb5, 0x56, 0xc9, 0x11, 0xe4, 0x2f, 0xf0, 0x2b, 0x2c, 0xc1, 0x49, 0x1a, 0xfb, 0xdd, 0x89, 0x3f, 0x18, 0x98, 0x78, 0x13, 0x83, 0xf4, 0x30, 0xe2, 0x4e, 0x0e, 0xf4, 0x6c, 0xcb, 0xc6, 0xc7, 0x31, 0xe9, 0x78, 0x74, 0xfd, 0x53, 0x05, 0x4e, 0x7b, 0xd3, 0x9b, 0xeb, 0x15, 0xc0, 0x6f, 0xbf, 0xa4, 0x69, 0x7d, 0xd1, 0x53, 0x0f, 0x0b, 0xc1, 0x2b, 0xad, 0x00, 0x44, 0x10, 0xe2, 0x9f, 0xb9, 0xf3, 0x0c, 0x98, 0x53, 0xf0, 0x60, 0xcb, 0xee, 0x7e, 0x5c, 0x83, 0x4a, 0xde, 0xa0, 0x7a, 0xcf, 0x50, 0x2b, 0x84, 0x09, 0xff, 0x42, 0xe4, 0x80, 0x2a, 0x64, 0x20, 0x9b, 0xb9, 0xba, 0xd4, 0x54, 0xca, 0xd8, 0xdc, 0x0a, 0x4d, 0xdd, 0x84, 0x91, 0x5e, 0x16, 0x90, 0x1d, 0xdc, 0xe3, 0x95, 0x55, 0xac, 0xf2, 0x8c, 0x9a, 0xcc, 0xb2, 0x6d, 0x17, 0x01, 0xe4, 0x01, 0xc6, 0xba, 0xe4, 0xb8, 0xd5, 0xbd, 0x7b, 0x43, 0xc9, 0x69, 0x6b, 0x40, 0xf7, 0xdc, 0x65, 0xa4, 0xf7, 0xca, 0x1f, 0xd8, 0xe5, 0xba, 0x4c, 0xdf, 0xe4, 0x64, 0x9e, 0x7d, 0xbd, 0x54, 0x13, 0x13,
        0xc6, 0x0c, 0xb8, 0x1d, 0x31, 0x0a, 0x49, 0xe2, 0x43, 0xb6, 0x95, 0x5f, 0x05, 0x6e, 0x66, 0xf4, 0x21, 0xa8, 0x65, 0xce, 0xf8, 0x8e, 0xcc, 0x16, 0x1e, 0xbb, 0xd8, 0x0e, 0xcb, 0xd2, 0x48, 0x37, 0xaf, 0x4e, 0x67, 0x45, 0xf1, 0x79, 0x69, 0xd2, 0xee, 0xa4, 0xb5, 0x01, 0xbf, 0x57, 0x0f, 0x68, 0x37, 0xbe, 0x4e, 0xff, 0xc9, 0xb9, 0x92, 0x23, 0x06, 0x75, 0xa0, 0x42, 0xe4, 0x0a, 0x30, 0xf0, 0xaf, 0xb0, 0x54, 0x88, 0x7c, 0xc0, 0xc1, 0x0c, 0x6d, 0x01, 0x36, 0x63, 0xf3, 0x3d, 0xbc, 0x72, 0xf6, 0x96, 0xc8, 0x87, 0xab, 0x8b, 0x0c, 0x91, 0x2f, 0x42, 0x2a, 0x11, 0xf6, 0x2d, 0x5e, 0x77, 0xce, 0x9c, 0xc1, 0x34, 0xe5, 0x2d, 0x9b, 0xd0, 0x37, 0x97, 0x0e, 0x39, 0xe5, 0xaa, 0xbe, 0x15, 0x3e, 0x6b, 0x1e, 0x73, 0xf6, 0xd7, 0xf4, 0xd6, 0x71, 0x70, 0xc6, 0xa1, 0xe6, 0x04, 0xd3, 0x7c, 0x2d, 0x1c, 0x98, 0x47, 0xdb, 0x8f, 0x59, 0x99, 0x2a, 0x57, 0x63, 0x14, 0xc7, 0x02, 0x42, 0x74, 0x57, 0x02, 0x22, 0xb2, 0x55, 0xe9, 0xf3, 0xe0, 0x76, 0x1c, 0x50, 0xbf, 0x43, 0x65, 0xbe, 0x52, 0xbd, 0x46, 0xf0, 0xfd, 0x5e, 0x25, 0xfe, 0x34, 0x50, 0x0d, 0x24, 0x7c, 0xfc, 0xfa, 0x82, 0x2f, 0x8c, 0x7d, 0x97, 0x1b, 0x07, 0x6b, 0x20, 0x6c, 0x9b, 0x7b, 0xae, 0xbf, 0xb3, 0x4f, 0x6e, 0xbb, 0xb6, 0xc4, 0xe9, 0xa5, 0x07, 0xa7, 0x74, 0x45, 0x16, 0x8a, 0x12, 0xee, 0x42, 0xc8, 0xea, 0xb5, 0x33, 0x69, 0xef, 0xff, 0x60, 0x6d, 0x99, 0xa3, 0x92, 0x5d, 0x0f, 0xbe, 0xb7, 0x4e, 0x1c, 0x85,
        0xef, 0x9e, 0x1d, 0x38, 0x72, 0x1f, 0xe0, 0xca, 0xc9, 0x90, 0x85, 0x3f, 0xa6, 0x5d, 0x60, 0x3f, 0xe6, 0x92, 0x08, 0x3b, 0xd4, 0xc3, 0xa2, 0x7e, 0x7c, 0x35, 0x49, 0xd4, 0x21, 0x38, 0x8c, 0x2c, 0x49, 0xb3, 0xcb, 0x33, 0xd4, 0xc2, 0x88, 0xdc, 0x09, 0xb3, 0x8a, 0x13, 0x95, 0x0f, 0xb4, 0x0a, 0xd1, 0x1d, 0xc8, 0xe4, 0x64, 0xb4, 0x24, 0x51, 0xe1, 0x0a, 0x22, 0xd4, 0x45, 0x77, 0x91, 0x0a, 0xc6, 0x61, 0xa1, 0x2c, 0x50, 0x84, 0x1c, 0x0c, 0xbe, 0x05, 0x1c, 0x3b, 0x4f, 0x27, 0x83, 0x33, 0xba, 0xfb, 0x7f, 0xa0, 0xc6, 0x38, 0xb4, 0x0c, 0x15, 0x49, 0x8f, 0xfa, 0x17, 0x76, 0xa9, 0x54, 0xf4, 0x6c, 0x7e, 0x5e, 0x39, 0xb8, 0xa8, 0x78, 0x86, 0x48, 0xb2, 0x18, 0xf1, 0xde, 0x0d, 0x24, 0xee, 0x6b, 0x01, 0x7d, 0x60, 0xfa, 0x35, 0xfe, 0x71, 0x0b, 0xfa, 0x8c, 0x79, 0x6c, 0x0b, 0x25, 0x84, 0x6d, 0x1a, 0x1d, 0xe0, 0x33, 0xa1, 0xa0, 0x8f, 0x47, 0x08, 0x4b, 0x5c, 0x8c, 0xc6, 0x1e, 0x2a, 0x6d, 0xd8, 0x3e, 0x09, 0x83, 0x96, 0xe6, 0xbc, 0x14, 0x55, 0x17, 0xcb, 0x50, 0x44, 0xdb, 0x80, 0xab, 0xb9, 0xf0, 0x1a, 0x3a, 0x9e, 0x23, 0xd5, 0x46, 0x73, 0x4b, 0xd0, 0x41, 0x9d, 0x29, 0x03, 0x59, 0x29, 0xeb, 0x82, 0x71, 0x09, 0x0c, 0x26, 0x10, 0x0f, 0x59, 0xd4, 0xd7, 0xb4, 0x4d, 0xe5, 0x35, 0xf5, 0x19, 0xef, 0xc7, 0xe7, 0x43, 0x0a, 0x3e, 0xeb, 0x3d, 0xc5, 0x55, 0xde, 0x04, 0xe7, 0x88, 0x72, 0x6c, 0xf7, 0x9d, 0x86, 0xb2, 0x0c, 0x83, 0x55, 0x20, 0x67, 0xc0, 0xc9, 0x15,
        0x3c, 0x76, 0x69, 0x80, 0x79, 0x68, 0x89, 0x16, 0x0a, 0xaf, 0xe4, 0x2c, 0xf0, 0x0e, 0x26, 0x74, 0x84, 0xfb, 0x27, 0xd4, 0x1c, 0x61, 0xbe, 0xe8, 0xc3, 0xce, 0x74, 0xd9, 0xf8, 0x5a, 0xa8, 0x63, 0x13, 0x27, 0xfa, 0xab, 0x93, 0x32, 0x25, 0x18, 0xb1, 0x78, 0x2f, 0xd3, 0x93, 0x0b, 0xc6, 0x5a, 0xda, 0xfe, 0xff, 0x7e, 0x38, 0x0c, 0x26, 0x44, 0x4c, 0x23, 0xe0, 0x8e, 0x64, 0xff, 0x07, 0xbc, 0x5b, 0x87, 0xd6, 0x3c, 0x8e, 0xe7, 0xd1, 0x78, 0x55, 0x00, 0x19, 0xbe, 0x98, 0x55, 0x1e, 0x16, 0xea, 0x63, 0x79, 0xb5, 0xaf, 0x9a, 0x20, 0x04, 0x8d, 0x3f, 0xdc, 0x15, 0x29, 0xc4, 0xe3, 0x9a, 0x82, 0x92, 0x85, 0xee, 0x1c, 0x37, 0xb3, 0xd7, 0xd2, 0x2e, 0x1e, 0xdb, 0x59, 0x87, 0xef, 0xa8, 0x9a, 0xaa, 0xa4, 0xed, 0x89, 0x33, 0xa8, 0xa7, 0x6c, 0x96, 0x9f, 0x26, 0xeb, 0xdc, 0x61, 0xc4, 0x8f, 0xd3, 0x2b, 0x81, 0x86, 0x6c, 0x9c, 0xc2, 0xb1, 0xb5, 0xbc, 0xa6, 0xd6, 0xd6, 0x1d, 0xce, 0x93, 0x78, 0xb3, 0xec, 0xa8, 0x64, 0x19, 0x13, 0x59, 0x1c, 0xb9, 0xbf, 0xd8, 0x7f, 0x27, 0x8e, 0x6f, 0x05, 0xd9, 0x1a, 0xa4, 0x1a, 0xc2, 0x46, 0x81, 0x52, 0xa5, 0xaf, 0x73, 0x35, 0x34, 0x88, 0x60, 0x46, 0x4d, 0x09, 0x87, 0xf1, 0x7e, 0x5e, 0xea, 0x32, 0x98, 0xb4, 0x68, 0x28, 0xff, 0x47, 0xde, 0x72, 0x9b, 0xc5, 0xfe, 0xb8, 0x93, 0xe8, 0x79, 0xe4, 0xa6, 0xd7, 0x63, 0x94, 0x29, 0x94, 0x33, 0x30, 0x61, 0xd4, 0x19, 0x36, 0x99, 0x94, 0x31, 0xbf, 0x93, 0x46, 0x04, 0xc0, 0xfe, 0x4d,
        0x92, 0xb4, 0xbc, 0xb2, 0x14, 0x3f, 0xf7, 0xce, 0x05, 0xcf, 0xf2, 0x5b, 0x66, 0xcb, 0x67, 0xa9, 0x8f, 0x63, 0xd4, 0x7c, 0x1d, 0x33, 0x6a, 0x05, 0xfb, 0xf7, 0x11, 0x03, 0x97, 0xff, 0x02, 0x1b, 0x6f, 0x15, 0x8b, 0x33, 0xe6, 0xf7, 0x5d, 0x93, 0x21, 0x9d, 0x17, 0xde, 0x9e, 0x87, 0xdc, 0xcd, 0x9a, 0x6a, 0x30, 0x3e, 0xa9, 0x70, 0xed, 0x93, 0x1d, 0x43, 0xb5, 0x5d, 0xb0, 0x46, 0x74, 0x73, 0x3b, 0x25, 0xfa, 0x0e, 0xe3, 0x70, 0x74, 0x2d, 0x75, 0xd6, 0x14, 0x67, 0x40, 0x31, 0xf9, 0x2c, 0xf6, 0x38, 0xea, 0x45, 0x33, 0xc1, 0xb6, 0xd5, 0x93, 0x0f, 0x5c, 0xaf, 0x3a, 0x53, 0x75, 0xd6, 0xe8, 0x97, 0xa0, 0x51, 0x3f, 0x96, 0x41, 0x32, 0x0b, 0x59, 0x48, 0xbf, 0x2b, 0x19, 0x67, 0x98, 0x42, 0xfe, 0x44, 0x23, 0x84, 0xa9, 0x09, 0x40, 0x4e, 0x10, 0x25, 0xdf, 0x68, 0x93, 0x6b, 0x0d, 0xa8, 0x51, 0x47, 0x55, 0xb7, 0xb8, 0x22, 0xab, 0xa3, 0x3c, 0x78, 0xd6, 0x8b, 0x4f, 0x2a, 0x73, 0xc1, 0x4a, 0x4a, 0xdd, 0x73, 0xb1, 0xc0, 0x8c, 0x5f, 0xf6, 0xe7, 0xbe, 0x9c, 0x96, 0xd6, 0x37, 0x91, 0x05, 0x52, 0xd1, 0x2f, 0xa9, 0xdc, 0xca, 0x11, 0x30, 0x6d, 0x4f, 0xb5, 0x6e, 0x39, 0x24, 0x28, 0x80, 0x54, 0x28, 0x87, 0xe6, 0x40, 0xeb, 0xd8, 0x7a, 0x1f, 0x63, 0x56, 0xc1, 0x4d, 0xa0, 0xf8
    };
    ASSERT_EQ(1537, (int)sizeof(c0c1));
    
    // c0
    EXPECT_EQ(0x03, c0c1[0]);
    
    // c1
    c1s1 c1;
    
    // the schema of data must be schema0: key-digest.
    ASSERT_EQ(ERROR_SUCCESS, c1.parse((char*)c0c1 + 1, 1536, srs_schema0));
    EXPECT_EQ((int32_t)0x000f64d0, c1.time);
    EXPECT_EQ((int32_t)0x80000702, c1.version);
    
    // manually validate the c1
    // @see: calc_c1_digest
    char* c1s1_joined_bytes = new char[1536 -32];
    SrsAutoFree(char, c1s1_joined_bytes);
    ASSERT_EQ(ERROR_SUCCESS, c1.payload->copy_to(&c1, c1s1_joined_bytes, 1536 - 32, false));
    
    bool is_valid;
    ASSERT_EQ(ERROR_SUCCESS, c1.c1_validate_digest(is_valid));
    ASSERT_TRUE(is_valid);
    
    // 128bytes key
    uint8_t key[] = {
        0x01, 0xc6, 0xba, 0xe4, 0xb8, 0xd5, 0xbd, 0x7b, 0x43, 0xc9, 0x69, 0x6b, 0x40, 0xf7, 0xdc, 0x65, 0xa4, 0xf7, 0xca, 0x1f, 0xd8, 0xe5, 0xba, 0x4c, 0xdf, 0xe4, 0x64, 0x9e, 0x7d, 0xbd, 0x54, 0x13, 0x13, 0xc6, 0x0c, 0xb8, 0x1d, 0x31, 0x0a, 0x49, 0xe2, 0x43, 0xb6, 0x95, 0x5f, 0x05, 0x6e, 0x66,
        0xf4, 0x21, 0xa8, 0x65, 0xce, 0xf8, 0x8e, 0xcc, 0x16, 0x1e, 0xbb, 0xd8, 0x0e, 0xcb, 0xd2, 0x48, 0x37, 0xaf, 0x4e, 0x67, 0x45, 0xf1, 0x79, 0x69, 0xd2, 0xee, 0xa4, 0xb5, 0x01, 0xbf, 0x57, 0x0f, 0x68, 0x37, 0xbe, 0x4e, 0xff, 0xc9, 0xb9, 0x92, 0x23, 0x06, 0x75, 0xa0, 0x42, 0xe4, 0x0a, 0x30,
        0xf0, 0xaf, 0xb0, 0x54, 0x88, 0x7c, 0xc0, 0xc1, 0x0c, 0x6d, 0x01, 0x36, 0x63, 0xf3, 0x3d, 0xbc, 0x72, 0xf6, 0x96, 0xc8, 0x87, 0xab, 0x8b, 0x0c, 0x91, 0x2f, 0x42, 0x2a, 0x11, 0xf6, 0x2d, 0x5e
    };
    EXPECT_TRUE(srs_bytes_equals(c1.get_key(), (char*)key, 128));
    
    // 32bytes digest
    uint8_t digest[] = {
        0x6c, 0x96, 0x9f, 0x26, 0xeb, 0xdc, 0x61, 0xc4, 0x8f, 0xd3, 0x2b, 0x81, 0x86, 0x6c, 0x9c, 0xc2,
        0xb1, 0xb5, 0xbc, 0xa6, 0xd6, 0xd6, 0x1d, 0xce, 0x93, 0x78, 0xb3, 0xec, 0xa8, 0x64, 0x19, 0x13
    };
    EXPECT_TRUE(srs_bytes_equals(c1.get_digest(), (char*)digest, 32));
}

VOID TEST(ProtocolHandshakeTest, ComplexHandshake)
{
    srs_error_t err;

    uint8_t c0c1[] = {
        0x03, 0x01, 0x14, 0xf7, 0x4e, 0x80, 0x00, 0x07, 0x02, 0xac, 0x14, 0x98, 0x57, 0x0a, 0x07, 0x58, 0x44, 0x96, 0x47, 0xb5, 0x9a, 0x73, 0xf6, 0x07, 0x0f, 0x49, 0x0d, 0x72, 0xb8, 0x16, 0xbb, 0xb2, 0xb7, 0x61, 0x17, 0x79, 0xa0, 0xe9, 0x98, 0xca, 0xb2, 0x86, 0x64, 0x5f, 0x65, 0x3e, 0xfc, 0x4d, 0xc0, 0x0e, 0x4c, 0xfa, 0x91, 0xc7, 0x0f, 0x2e, 0x57, 0x31, 0x4b, 0x96, 0xef, 0xc9, 0x81, 0x02, 0x00, 0x54, 0x25, 0x2b, 0xb2, 0x0d, 0x7c, 0xee, 0xba, 0xdb, 0xe4, 0x06, 0x78, 0xcd, 0x70, 0x2c, 0x54, 0x5a, 0x3a, 0x03, 0x13, 0x2e, 0xe7, 0x4b, 0x87, 0x40, 0x77, 0x0b, 0x9f, 0xd2, 0xab, 0x32, 0x07, 0x6f, 0x1e, 0x75, 0x74, 0xe9, 0xc7, 0x44, 0xd9, 0x76, 0x53, 0xba, 0xe2, 0x52, 0xfa, 0xcc, 0xef, 0x34, 0xd5, 0x14, 0x61, 0xac, 0xcc, 0x63, 0xfd, 0x2b, 0x2d, 0xb3, 0xb8, 0xdd, 0x8a, 0x51, 0x9a, 0x2d, 0x0e, 0xfa, 0x84, 0x25, 0x55, 0xb2, 0xb7, 0x94, 0x54, 0x68, 0xfb, 0x94, 0xdf, 0xd8, 0xeb, 0x43, 0xd0, 0x11, 0x70, 0x8f, 0xf5, 0x48, 0xfc, 0x69, 0x4d, 0x5b, 0xc6, 0x53, 0x8a, 0x22, 0xea, 0x62, 0x84, 0x89, 0x6b, 0xfe, 0x4e, 0xab, 0x51, 0x98, 0xf4, 0x4f, 0xae, 0xf8, 0xdf, 0xac, 0x43, 0xed, 0x5a, 0x04, 0x97, 0xc4, 0xbe, 0x44, 0x5b, 0x99, 0x20, 0x68, 0x67, 0x0f, 0xe3, 0xfa, 0x4c, 0x9d, 0xe7, 0x0b, 0x3f, 0x80, 0x7c, 0x4c, 0x35, 0xf6, 0xdd, 0x20, 0x05, 0xfd, 0x0f, 0x39, 0xb7, 0x36, 0x45, 0x4c, 0xb7, 0x62, 0x92, 0x35, 0x2a, 0xcd, 0xb9, 0x49, 0xea, 0x12, 0x0b, 0x5f, 0x39, 0xae, 0x3b, 0x49, 0x29, 0xe6, 0x30, 0xc7, 0x7c, 0x77, 0xaf, 0x00, 0x43, 0x4d, 0x06, 0x45, 0x72, 0x73, 0x25, 0x71, 0x5e, 0x35, 0x04, 0xbd, 0xe9, 0x48, 0x23, 0x64, 0x4d, 0x15, 0x0b, 0xc5, 0x3f, 0x6e, 0x3a, 0xd5, 0xd5, 0xa6, 0xae, 0x3b, 0x4c, 0x66, 0x6a, 0x70, 0x8b, 0xf3, 0x6a, 0x43, 0xc4, 0xb9, 0xbd, 0xa0, 0x09, 0x72, 0xbc, 0xce, 0x7a, 0xea, 0x49, 0xf2, 0x86, 0xa7, 0xd8, 0x4a, 0x87, 0x28, 0xca, 0x2c, 0x53, 0xee, 0x96, 0x0b, 0xbe, 0x15, 0x14, 0xa8, 0x00, 0xca, 0x76, 0x08, 0x4d, 0x0f, 0xef, 0x78, 0x4b, 0xf6, 0x47, 0x60, 0xfc, 0x16, 0x00, 0x7c, 0x6b, 0x49, 0x39, 0x64, 0x36, 0xee, 0x45, 0x3a, 0x9a, 0xa5, 0xbf, 0xfb, 0x7b, 0xe7, 0xcf, 0x42, 0x82, 0x48, 0x1b, 0x30, 0xfe, 0x0d, 0xba, 0x10, 0xb8, 0xe1, 0x40, 0xcc, 0x6f, 0x36, 0x1c, 0x94, 0x5d, 0x50, 0x9e, 0x21, 0x08, 0xc9, 0xd5, 0xb0, 0x32, 0x51, 0x6a, 0x8f, 0xfa, 0x57, 0x8d, 0x45, 0xd7, 0xd2, 0xd0, 0xd6, 0x6c, 0x78, 0x95, 0xe9, 0xe1, 0x20, 0x97, 0x1a, 0x43, 0x40, 0xa3, 0xb5, 0xcc, 0x4b, 0x12, 0x84, 0x1e, 0x0e, 0xd3, 0x32, 0xca, 0x99, 0xc3, 0x2b, 0x78, 0x17, 0x24, 0x6b, 0xc7, 0xbc, 0x9d, 0x05, 0xc6, 0xaf, 0x8f, 0x19, 0x75, 0x3c, 0x08, 0xa6, 0x08, 0x26, 0x5b, 0xf4, 0x10, 0x40, 0xaa, 0x6a, 0x7e, 0xb9, 0xde, 0x0b, 0x23, 0x3f, 0x53, 0x5a, 0x20, 0x13, 0x62, 0xec, 0x53, 0x86, 0x81, 0x1f, 0xf6, 0x8e, 0xe3, 0xd1, 0xaa, 0xb5, 0x41, 0x87, 0x62, 0xd2, 0xb7, 0x09, 0x12, 0x71, 0x01, 0x2c, 0xac, 0x6d, 0x9d, 0x37, 0x46, 0x5b, 0xdc, 0x76, 0x2c, 0x96, 0x61, 0x88, 0x55, 0x5a, 0x20, 0xc2, 0x84, 0x95, 0xbd, 0x72, 0xc4, 0xb7, 0x22, 0xae, 0xeb, 0x49, 0x0e, 0x16, 0xf1, 0xf1, 0xbf, 0xc5, 0xc7, 0xa8, 0x8d, 0xfb, 0xe1, 0x08, 0x6c, 0xc4, 0x79, 0x81, 0x13, 0xe8, 0x39, 0xbf, 0x6e, 0x5c, 0xa1, 0x62, 0xfb, 0x32, 0x2a, 0x62, 0xf0, 0x12, 0x07, 0x31, 0x93, 0x40, 0xf3, 0xc0, 0xea, 0x1d, 0xd8, 0x65, 0xba, 0x12, 0xb3, 0x9b, 0xf5, 0x59, 0x9c, 0x4e, 0xf6, 0xb9, 0xf7, 0x85, 0xa1, 0xd9, 0x2f, 0x7c, 0x8b, 0xd0, 0xfc, 0x53, 0x3b, 0xed, 0x85, 0xa4, 0xd2, 0x5e, 0x69, 0x61, 0x02, 0x53, 0xb6, 0x19, 0xc7, 0x82, 0xea, 0x8a, 0x45, 0x01, 0x5d, 0x4b, 0xb3, 0x06, 0x86, 0x7f, 0x4b, 0x2f, 0xe7, 0xa8, 0xd0, 0x28, 0x62, 0x02, 0xe8, 0xf3, 0x9e, 0x1e, 0x72, 0x82, 0x07, 0x9f, 0xdd, 0xd2, 0x83, 0x7d, 0x89, 0x73, 0x1b, 0x6f, 0x35, 0x20, 0xb7, 0x88, 0x15, 0x92, 0xa7, 0x11, 0xfe, 0x81, 0x68, 0xed, 0x14, 0x07, 0xdf, 0x4a, 0x06, 0x9c, 0x5e, 0x7e, 0x34, 0x3a, 0x2a, 0x8a, 0xd3, 0xe8, 0xf8, 0xd4, 0xdb, 0xe3, 0xe9, 0x73, 0xbf, 0xa7, 0xe9, 0x73, 0x62, 0xf2, 0x9d, 0xc1, 0xf7, 0x51, 0xeb, 0xff, 0xb7, 0xe6, 0xd9, 0xac, 0x46, 0x06, 0x74, 0xe2, 0x25, 0x3f, 0x46, 0x43, 0xce, 0x49, 0x52, 0x25, 0x1b, 0xf9, 0x24, 0x5c, 0xda, 0xfd, 0x7f, 0xf6, 0xef, 0xb3, 0xd5, 0xe9, 0x6e, 0x35, 0xb8, 0xd1, 0x0e, 0x2c, 0xc1, 0x48, 0x5a, 0x27, 0x0a, 0x81, 0x01, 0x0f, 0xe4, 0x51, 0xcf, 0x89, 0x36, 0xd3, 0xe8, 0x5e, 0x05, 0xb9, 0x83, 0x42, 0xf3, 0xa5, 0x94, 0x67, 0x6d, 0x6a, 0x6e, 0xad, 0xf8, 0x90, 0xb1, 0x1d, 0x63, 0x18, 0x52, 0xc1, 0xbf, 0xbc, 0xad, 0xf4, 0xd2, 0xc5, 0xef, 0xca, 0x4c, 0xfe, 0xa1, 0xda, 0x15, 0x92, 0x4c, 0x42, 0x3d, 0xfc, 0x80, 0x7e, 0x49, 0x13, 0x4e, 0xf6, 0xe1, 0xee, 0x70, 0xca, 0xd9, 0x0a, 0xde, 0x9b, 0xea, 0xcd, 0xf9, 0x90, 0xfd, 0xae, 0x09, 0xce, 0xb6, 0xa0, 0xf7, 0xd1, 0xe6, 0x0c, 0x55, 0x1e, 0x3f, 0xbb, 0x1e, 0xff, 0x3d, 0xdb, 0xdd, 0x27, 0x80, 0x06, 0x53, 0x7e, 0x0b, 0x2a, 0x80, 0x24, 0x51, 0x5c, 0x6a, 0xab, 0x32, 0x5d, 0x37, 0x8a, 0xf4, 0xb7, 0x11, 0xa7, 0xc1, 0x9e, 0x05, 0x2c, 0x16, 0xc2, 0x08, 0xe2, 0xac, 0x1a, 0xeb, 0x60, 0xf8, 0xd2, 0xea, 0x39, 0x01, 0x1c, 0x64, 0xbd, 0x22, 0x80, 0x19, 0x20, 0xc9, 0x6f, 0xdd, 0x5c, 0x73, 0x8c, 0xa1, 0x53, 0x48, 0x2e, 0x99, 0x1d, 0xc0, 0x8f, 0x28, 0xf1, 0xe3, 0xc5, 0xc5, 0x65, 0x53, 0xf2, 0x44, 0x44, 0x24, 0xb9, 0xe2, 0x73, 0xe4, 0x76, 0x14, 0x56, 0xb8, 0x82, 0xe3, 0xb4, 0xfd, 0x68, 0x31, 0xed, 0x40, 0x10, 0x99, 0xd3, 0x3d, 0xe5, 0x6b, 0x14, 0x61, 0x66, 0x9a, 0xf6, 0x33, 0x98, 0xc5, 0x4d, 0x11, 0xbb, 0xf8, 0x56, 0xf8, 0x8f, 0xd7, 0xb9, 0xda, 0xa3, 0x56, 0x1a, 0xe0, 0x9e, 0xbe, 0x5f, 0x56, 0xe5, 0xb9, 0xd8, 0xf3, 0xbc, 0x19, 0xf5, 0xe9, 0x1f, 0xd2, 0xea, 0xf4, 0x5a, 0xde, 0xed, 0xd4, 0x9e, 0xc8, 0xf5, 0x54,
        0x83, 0x8b, 0x8c, 0x2d, 0x24, 0x0e, 0x30, 0xb1, 0x84, 0xa2, 0xbe, 0x2c, 0x86, 0xe6, 0x42, 0x82, 0xaa, 0x37, 0x64, 0x55, 0x51, 0xbc, 0xde, 0xc0, 0x63, 0x88, 0xf6, 0x31, 0x71, 0x52, 0xd5, 0x34, 0x0f, 0x8e, 0xcb, 0x28, 0x65, 0x93, 0x1a, 0x66, 0x3b, 0x21, 0x00, 0xaa, 0x7a, 0xda, 0x2d, 0xf6, 0x7e, 0xb5, 0x27, 0x79, 0xf4, 0x50, 0x3b, 0x10, 0x6b, 0x3c, 0xd7, 0x99, 0x9d, 0xf6, 0xc5, 0x01, 0x91, 0xa0, 0xd5, 0x4f, 0xd3, 0x76, 0x54, 0xa8, 0x5c, 0x35, 0x1d, 0xe2, 0x35, 0x6a, 0x68, 0x67, 0x03, 0xc4, 0x1f, 0xe9, 0x60, 0xb8, 0x49, 0xb1, 0x9a, 0x40, 0xd9, 0x3c, 0x4c, 0x73, 0xaa, 0x88, 0x63, 0xaf, 0xfe, 0xe8, 0xa8, 0x0c, 0x96, 0xbe, 0xb4, 0x65, 0x7c, 0x27, 0xfb, 0xc1, 0x27, 0x24, 0x58, 0xab, 0x4b, 0xa0, 0x5a, 0x7d, 0xc7, 0xca, 0x2d, 0xa5, 0x22, 0xa7, 0xed, 0x26, 0x87, 0xd5, 0x44, 0x1a, 0xc7, 0xdd, 0xfb, 0x60, 0xfc, 0xe5, 0x50, 0xd9, 0x8d, 0xa7, 0xdb, 0x78, 0xb6, 0x9d, 0x80, 0x0f, 0xb9, 0x5f, 0xa7, 0x53, 0x92, 0x5d, 0x18, 0xce, 0x89, 0xc2, 0x69, 0xee, 0xcf, 0xb6, 0x66, 0xe5, 0x66, 0xd2, 0xe3, 0x35, 0x74, 0x0b, 0x83, 0xb6, 0xde, 0xf1, 0xfb, 0xb4, 0x1d, 0x4b, 0x94, 0x95, 0x06, 0x82, 0xe7, 0x1c, 0xf8, 0xc5, 0xe6, 0xd0, 0xf2, 0x17, 0x37, 0x44, 0xfe, 0x99, 0x43, 0x82, 0xbb, 0x88, 0xe4, 0x43, 0x67, 0xcc, 0x4d, 0x5f, 0xa6, 0x26, 0xd7, 0x53, 0xd6, 0x45, 0x96, 0x2b, 0x63, 0xd1, 0x2a, 0xa1, 0x2c, 0x41, 0x59, 0x8b, 0xb8, 0xc1, 0x89, 0x03, 0x3a, 0x61, 0x13, 0xc4, 0x2c, 0x37, 0xa5, 0xbf, 0xd7, 0xdb, 0xd8, 0x53, 0x5f, 0xa1, 0xdb, 0xdb, 0xa5, 0x73, 0xb6, 0xf7, 0x74, 0xa0, 0xf8, 0x93, 0xf5, 0x61, 0xee, 0x3c, 0xe7, 0x00, 0x01, 0x98, 0xe0, 0xa1, 0x22, 0xb6, 0x9a, 0x83, 0x44, 0xa1, 0xe6, 0x70, 0x56, 0x65, 0x92, 0x1e, 0xf0, 0xbc, 0x73, 0xa5, 0x7a, 0xc1, 0x1a, 0x02, 0xf9, 0xd4, 0xc4, 0x7c, 0x81, 0xda, 0x15, 0xc0, 0xd4, 0x25, 0xdc, 0x17, 0xa6, 0x0d, 0x90, 0x55, 0xf2, 0x10, 0xf8, 0xa7, 0x71, 0x9b, 0xed, 0xdf, 0xdf, 0xa1, 0xe4, 0xb9, 0x12, 0x6b, 0x05, 0x3e, 0x83, 0x99, 0x49, 0xbf, 0x66, 0xbb, 0xf6, 0x76, 0xd3, 0xa9, 0x24, 0x61, 0x8c, 0x25, 0x49, 0xd0, 0xf7, 0x83, 0x44, 0xfb, 0x27, 0xe2, 0x7d, 0x69, 0x6d, 0x34, 0x67, 0xed, 0x39, 0x89, 0x02, 0xcb, 0x2f, 0x33, 0x3c, 0xcd, 0x12, 0x42, 0x8f, 0x86, 0x7d, 0xda, 0x3f, 0xd7, 0x26, 0x62, 0x9c, 0x1f, 0x2e, 0xa8, 0xc3, 0x85, 0xf1, 0x73, 0xe5, 0x2c, 0x11, 0xde, 0x98, 0xc8, 0xb0, 0x10, 0x17, 0x55, 0xf5, 0x32, 0x52, 0x67, 0xca, 0x64, 0x50, 0x28, 0x9a, 0x24, 0x92, 0xa1, 0x97, 0x57, 0x81, 0xaf, 0xca, 0x1e, 0xc0, 0xa4, 0x71, 0x2d, 0x2a, 0xec, 0xc9, 0x23, 0x6a, 0x0c, 0x1d, 0x54, 0x15, 0x2a, 0x56, 0x42, 0x0a, 0x83, 0xff, 0x28, 0xba, 0xe7, 0x68, 0x38, 0xf5, 0x32, 0xa9, 0xb7, 0xe7, 0x70, 0x32, 0xa8, 0x79, 0x5e, 0x46, 0x1d, 0xec, 0x29, 0x8a, 0xde, 0x41, 0x94, 0x94, 0x26, 0x79, 0xc2, 0x52, 0x23, 0xe0, 0xa1, 0x1d, 0x65, 0x0c, 0xbe, 0x1b, 0x87, 0x2a, 0x21, 0x53, 0x2f, 0x35, 0x56, 0xe8, 0xd1, 0x7b, 0xb8, 0x23, 0x75, 0x56, 0xc7, 0x08, 0x9d, 0x13, 0xf0, 0x8f, 0x80, 0x38, 0xe9, 0x92, 0xf7, 0x16, 0xc2, 0xf3, 0x74, 0xa7, 0x92, 0xf5, 0x49, 0x7d, 0x09, 0x41, 0xbc, 0x07, 0x61, 0x1f, 0xe6, 0xa0, 0xd8, 0xa6, 0xe3, 0x72, 0xa4, 0x59, 0x4a, 0xd9, 0x33, 0x40, 0x80, 0x3a, 0x3a, 0xb3, 0xa0, 0x96, 0xca, 0x56, 0x98, 0xbd, 0x1f, 0x80, 0x86, 0x6c, 0xe1, 0x09, 0x64, 0x1b, 0x1a, 0xc9, 0x52, 0xaa, 0xd1, 0x39, 0xea, 0x4b, 0x6a, 0x3e, 0x4e, 0xa4, 0xea, 0x00, 0xde, 0x07, 0x0b, 0x23, 0xbc, 0x40, 0xc4, 0xd2, 0xd9, 0xf6, 0xda, 0x8e, 0x22, 0x36, 0xbe, 0x5e, 0x65, 0x6e, 0xbe, 0xc8, 0xb0, 0x07, 0xa2, 0x2d, 0xe9, 0x4b, 0x73, 0x54, 0xe6, 0x0a, 0xf2, 0xd3, 0x83, 0x8b, 0x27, 0x4c, 0xcc, 0x0c, 0x8a, 0xd4, 0x2b, 0xb8, 0x95, 0x2e, 0x42, 0x64, 0x29, 0xc1, 0xe0, 0x6b, 0x92, 0xab, 0xfe, 0x53, 0x06, 0x96, 0x4a, 0x8c, 0x5d, 0x7c, 0x51, 0x74, 0xd0, 0x1e, 0x37, 0x35, 0x9c, 0x1e, 0x69, 0x8f, 0x68, 0x18, 0xd9, 0xbe, 0xaf, 0x81, 0x9b, 0x7e, 0xd8, 0x71, 0x9d, 0xb6, 0x50, 0x43, 0x78, 0x85, 0x7d, 0x65, 0x93, 0x45, 0xb4, 0x02, 0xd0, 0x5c, 0x36, 0xe2, 0x62, 0x3f, 0x40, 0x33, 0xee, 0x91, 0xe5, 0x3f, 0x67, 0x39, 0x2f, 0x1b, 0x89, 0x9f, 0x04, 0x9d, 0x46, 0x3e, 0x70, 0x92, 0x9e, 0x8c, 0xf5
    };
    uint8_t s0s1s2[] = {
        0x03, 0xac, 0x44, 0x29, 0x53, 0x04, 0x05, 0x00, 0x01, 0x6e, 0x65, 0x69, 0x2d, 0x69, 0x2d, 0x69, 0x73, 0x6e, 0x69, 0x73, 0x6c, 0x65, 0x72, 0x69, 0x72, 0x76, 0x65, 0x72, 0x69, 0x77, 0x74, 0x2e, 0x6e, 0x72, 0x76, 0x72, 0x65, 0x72, 0x70, 0x72, 0x69, 0x69, 0x70, 0x72, 0x73, 0x6e, 0x65, 0x72, 0x72, 0x6e, 0x2d, 0x65, 0x74, 0x72, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x40, 0x69, 0x69, 0x76, 0x77, 0x2d, 0x73, 0x65, 0x72, 0x72, 0x76, 0x73, 0x72, 0x2e, 0x2d, 0x76, 0x65, 0x31, 0x65, 0x6d, 0x6d, 0x73, 0x69, 0x73, 0x74, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x72, 0x65, 0x2d, 0x74, 0x69, 0x31, 0x65, 0x2d, 0x6f, 0x77, 0x2e, 0x76, 0x77, 0x2d, 0x77, 0x72, 0x65, 0x65, 0x31, 0x74, 0x73, 0x70, 0x74, 0x6e, 0x72, 0x6e, 0x73, 0x6d, 0x2e, 0x69, 0x72, 0x2d, 0x65, 0x69, 0x77, 0x69, 0x76, 0x72, 0x77, 0x72, 0x32, 0x6e, 0x65, 0x6c, 0x2e, 0x2d, 0x6e, 0x69, 0x6d, 0x6c, 0x73, 0x65, 0x73, 0x70, 0x2d, 0x65, 0x72, 0x40, 0x72, 0x74, 0x6e, 0x6e, 0x6d, 0x6f, 0x70, 0x74, 0x73, 0x2d, 0x63, 0x69, 0x32, 0x31, 0x2d, 0x40, 0x69, 0x70, 0x2d, 0x2d, 0x72, 0x69, 0x74, 0x63, 0x6f, 0x69, 0x69, 0x65, 0x6e, 0x32, 0x6f, 0x6c, 0x6e, 0x72, 0x73, 0x77, 0x65, 0x65, 0x72, 0x32, 0x6d, 0x65, 0x6c, 0x2d, 0x72, 0x6e, 0x65, 0x6d, 0x31, 0x65, 0x74, 0x2d, 0x6f, 0x72, 0x65, 0x63, 0x69, 0x40, 0x70, 0x2d, 0x65, 0x6d, 0x2d, 0x77, 0x63, 0x63, 0x74, 0x40, 0x36, 0x2d, 0x72, 0x65, 0x70, 0x2d, 0x6e, 0x69, 0x6d, 0x65, 0x74, 0x70, 0x76, 0x40, 0x76, 0x72, 0x72, 0x69, 0x77, 0x76, 0x69, 0x74, 0x74, 0x65, 0x31, 0x6d, 0x2e, 0x6f, 0x72, 0x73, 0x73, 0x6c, 0x40, 0x36, 0x72, 0x70, 0x72, 0x70, 0x72, 0x69, 0x32, 0x6c, 0x77, 0x70, 0x76, 0x65, 0x72, 0x76, 0x63, 0x65, 0x65, 0x77, 0x72, 0x6e, 0x2e, 0x76, 0x69, 0x69, 0x2e, 0x40, 0x72, 0x2e, 0x2e, 0x72, 0x73, 0x6e, 0x72, 0x72, 0x6e, 0x70, 0x40, 0x77, 0x65, 0x77, 0x65, 0x70, 0x63, 0x74, 0x2d, 0x70, 0x72, 0x2d, 0x74, 0x72, 0x31, 0x65, 0x6e, 0x2d, 0x76, 0x2d, 0x2d, 0x2d, 0x74, 0x76, 0x2d, 0x74, 0x65, 0x2e, 0x2d, 0x6c, 0x76, 0x2d, 0x6c, 0x70, 0x73, 0x6d, 0x65, 0x72, 0x31, 0x31, 0x36, 0x76, 0x73, 0x73, 0x6e, 0x2d, 0x6e, 0x73, 0x72, 0x2d, 0x6f, 0x6c, 0x65, 0x74, 0x77, 0x65, 0x69, 0x72, 0x69, 0x65, 0x6d, 0x76, 0x31, 0x65, 0x73, 0x72, 0x6c, 0x72, 0x77, 0x65, 0x76, 0x74, 0x72, 0x69, 0x72, 0x76, 0x32, 0x73, 0x6d, 0x72, 0x2d, 0x6d, 0x40, 0x69, 0x40, 0x69, 0x31, 0x69, 0x6f, 0x6e, 0x6d, 0x69, 0x73, 0x70, 0x72, 0x77, 0x6f, 0x6f, 0x65, 0x77, 0x76, 0x70, 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x32, 0x36, 0x6c, 0x74, 0x6e, 0x72, 0x74, 0x2d, 0x6e, 0x6c, 0x72, 0x72, 0x2d, 0x74, 0x65, 0x73, 0x70, 0x65, 0x72, 0x6c, 0x65, 0x65, 0x2d, 0x6e, 0x70, 0x6e, 0x40, 0x65, 0x6e, 0x6e, 0x74, 0x65, 0x6e, 0x72, 0x6e, 0xfe, 0x5a, 0x38, 0x79, 0x81, 0xe8, 0x49, 0xee, 0x93, 0xbb, 0xa0, 0x59, 0x4a, 0xa0, 0xcc, 0x31, 0xbf, 0x0d, 0x86, 0xc0, 0x3f, 0xae, 0x2a, 0x16, 0xfa, 0xf0, 0x4e, 0x0f, 0xa3, 0x01, 0x06, 0xa0, 0x0e, 0xa5, 0x8c, 0xa4, 0xca, 0xd2, 0x01, 0xa5, 0x90, 0xbd, 0x55, 0xd1, 0x42, 0x2b, 0xd4, 0xb3, 0xbb, 0x06, 0xb1, 0x3a, 0x94, 0x41, 0x76, 0x1d, 0xa5, 0x23, 0x6e, 0x1e, 0x59, 0x73, 0x63, 0x34, 0x60, 0xd3, 0x48, 0xc0, 0x3b, 0xcf, 0xf1, 0xa8, 0x38, 0xd6, 0xf3, 0x5e, 0x6d, 0xcb, 0xea, 0xfc, 0x9c, 0x52, 0xae, 0x9a, 0x89, 0xdb, 0x24, 0x1b, 0x92, 0x4a, 0x85, 0x97, 0x3c, 0xd8, 0x4c, 0x31, 0xad, 0xfd, 0x00, 0xef, 0xc5, 0x17, 0xa5, 0x22, 0xc0, 0xf1, 0x94, 0x18, 0xec, 0xf6, 0x49, 0xe5, 0x05, 0x11, 0x12, 0x67, 0x6c, 0x71, 0xc0, 0x84, 0x6d, 0x50, 0xf8, 0x23, 0x01, 0x57, 0xc4, 0xfc, 0x73, 0x65, 0x69, 0x6e, 0x65, 0x72, 0x6d, 0x6f, 0x69, 0x2d, 0x65, 0x65, 0x69, 0x63, 0x63, 0x69, 0x2d, 0x72, 0x2d, 0x69, 0x2d, 0x2d, 0x77, 0x72, 0x76, 0x72, 0x72, 0x2d, 0x76, 0x70, 0x63, 0x69, 0x74, 0x73, 0x6d, 0x65, 0x6c, 0x2d, 0x73, 0x6c, 0x65, 0x6e, 0x73, 0x77, 0x69, 0x63, 0x69, 0x70, 0x31, 0x40, 0x72, 0x69, 0x2d, 0x2d, 0x2d, 0x72, 0x72, 0x6c, 0x72, 0x63, 0x72, 0x77, 0x6e, 0x6c, 0x2d, 0x72, 0x2e, 0x76, 0x72, 0x65, 0x6d, 0x76, 0x36, 0x6d, 0x72, 0x77, 0x72, 0x65, 0x65, 0x69, 0x72, 0x76, 0x6d, 0x76, 0x74, 0x76, 0x72, 0x65, 0x69, 0x72, 0x6e, 0x6d, 0x77, 0x6c, 0x40, 0x32, 0x70, 0x65, 0x65, 0x69, 0x72, 0x31, 0x2e, 0x70, 0x36, 0x31, 0x65, 0x70, 0x72, 0x72, 0x73, 0x72, 0x6e, 0x6e, 0x73, 0x32, 0x2d, 0x2d, 0x2d, 0x69, 0x65, 0x31, 0x74, 0x6e, 0x65, 0x74, 0x65, 0x76, 0x69, 0x6d, 0x6c, 0x6e, 0x70, 0x74, 0x73, 0x72, 0x6d, 0x72, 0x72, 0x69, 0x65, 0x74, 0x65, 0x65, 0x2d, 0x70, 0x74, 0x6e, 0x74, 0x65, 0x6f, 0x72, 0x69, 0x76, 0x40, 0x31, 0x69, 0x72, 0x6d, 0x6d, 0x77, 0x69, 0x72, 0x65, 0x6e, 0x40, 0x63, 0x40, 0x65, 0x65, 0x69, 0x2d, 0x72, 0x65, 0x40, 0x69, 0x32, 0x74, 0x73, 0x6e, 0x36, 0x2d, 0x70, 0x65, 0x6c, 0x70, 0x6e, 0x72, 0x69, 0x32, 0x65, 0x74, 0x76, 0x77, 0x73, 0x6f, 0x77, 0x65, 0x72, 0x2d, 0x6e, 0x73, 0x65, 0x65, 0x70, 0x65, 0x2d, 0x65, 0x73, 0x2d, 0x65, 0x2e, 0x73, 0x69, 0x67, 0x45, 0x8b, 0x6b, 0x3b, 0xc9, 0x5f, 0x09, 0x65, 0x65, 0x72, 0x6c, 0x73, 0x6d, 0x70, 0x70, 0x73, 0x63, 0x70, 0x40, 0x72, 0x76, 0x65, 0x6e, 0x6f, 0x6c, 0x69, 0x2e, 0x72, 0x73, 0x76, 0x69, 0x77, 0x72, 0x2d, 0x69, 0x6e, 0x69, 0x65, 0x77, 0x73, 0x69, 0x70, 0x77, 0x63, 0x65, 0x74, 0x72, 0x73, 0x31, 0x65, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x6d, 0x65, 0x36, 0x6e, 0x6e, 0x69, 0x6d, 0x6e, 0x70, 0x77, 0x72, 0x65, 0x31, 0x65, 0x6e, 0x6e, 0x65, 0x2d, 0x65, 0x65, 0x2e, 0x77, 0x6e, 0x6f, 0x2d, 0x76, 0x65, 0x72, 0x6c, 0x31, 0x76, 0x65, 0x72, 0x2d, 0x36, 0x6c, 0x70, 0x6f, 0x65, 0x72, 0x73, 0x63, 0x72, 0x77, 0x73, 0x72, 0x65, 0x65, 0x65, 0x6c, 0x76, 0x72, 0x65, 0x6e, 0x65, 0x2e, 0x6f, 0x2d, 0x72, 0x70, 0x65, 0x74, 0x72,
        0x77, 0x69, 0x69, 0x72, 0x65, 0x77, 0x6c, 0x72, 0x2d, 0x69, 0x72, 0x31, 0x6e, 0x65, 0x70, 0x72, 0x74, 0x76, 0x6c, 0x2e, 0x72, 0x65, 0x72, 0x6c, 0x73, 0x6c, 0x2e, 0x2e, 0x72, 0x2d, 0x6e, 0x63, 0x32, 0x2e, 0x65, 0x2d, 0x65, 0x69, 0x2d, 0x65, 0x70, 0x6e, 0x72, 0x72, 0x32, 0x2e, 0x73, 0x70, 0x77, 0x65, 0x73, 0x77, 0x73, 0x40, 0x40, 0x73, 0x63, 0x2e, 0x65, 0x76, 0x70, 0x65, 0x69, 0x65, 0x70, 0x73, 0x40, 0x65, 0x73, 0x2d, 0x2d, 0x2e, 0x2e, 0x73, 0x65, 0x6f, 0x65, 0x65, 0x6d, 0x76, 0x70, 0x6d, 0x69, 0x70, 0x70, 0x69, 0x2e, 0x76, 0x6e, 0x72, 0x72, 0x72, 0x6d, 0x73, 0x6f, 0x73, 0x72, 0x72, 0x72, 0x77, 0x70, 0x65, 0x69, 0x72, 0x73, 0x6e, 0x69, 0x65, 0x65, 0x74, 0x65, 0x69, 0x40, 0x63, 0x69, 0x70, 0x6c, 0x6e, 0x2d, 0x65, 0x69, 0x72, 0x63, 0x6c, 0x72, 0x2e, 0x36, 0x69, 0x72, 0x6c, 0x6c, 0x2d, 0x6f, 0x76, 0x69, 0x6f, 0x2d, 0x6d, 0x6c, 0x72, 0x72, 0x2e, 0x70, 0x73, 0x6d, 0x6f, 0x2e, 0x6e, 0x69, 0x65, 0x65, 0x2d, 0x6d, 0x76, 0x6e, 0x69, 0x73, 0x73, 0x73, 0x74, 0x63, 0x65, 0x76, 0x2e, 0x77, 0x2d, 0x36, 0x73, 0x69, 0x2d, 0x72, 0x72, 0x6c, 0x36, 0x74, 0x72, 0x6d, 0x65, 0x2d, 0x65, 0x2e, 0x6d, 0x31, 0x72, 0x6f, 0x74, 0x76, 0x31, 0x65, 0x6d, 0x69, 0x72, 0x69, 0x69, 0x2d, 0x72, 0x73, 0x72, 0x72, 0x76, 0x31, 0x6e, 0x2d, 0x69, 0x6e, 0x77, 0x70, 0x69, 0x72, 0x6e, 0x76, 0x74, 0x6f, 0x65, 0x63, 0x6f, 0x73, 0x65, 0x73, 0x72, 0x69, 0x69, 0x40, 0x6e, 0x65, 0x65, 0x65, 0x65, 0x77, 0x70, 0x70, 0x6e, 0x72, 0x6e, 0x65, 0x72, 0x32, 0x65, 0x2d, 0x77, 0x69, 0x6e, 0x70, 0x69, 0x6f, 0x76, 0x77, 0x72, 0x74, 0x77, 0x6e, 0x72, 0xfe, 0x98, 0xf3, 0xb4, 0xff, 0x3f, 0x2e, 0xdb, 0x59, 0xbd, 0x32, 0x02, 0x6a, 0x44, 0x03, 0x67, 0x9e, 0xe1, 0x98, 0x97, 0xed, 0x67, 0x6d, 0xb0, 0x8f, 0xa9, 0xb6, 0xf8, 0x4d, 0x92, 0x35, 0x19, 0x72, 0x72, 0x65, 0x74, 0x73, 0x6e, 0x65, 0x65, 0x69, 0x36, 0x72, 0x73, 0x2d, 0x70, 0x2d, 0x2d, 0x69, 0x6e, 0x72, 0x65, 0x32, 0x72, 0x77, 0x72, 0x73, 0x77, 0x73, 0x70, 0x2d, 0x2d, 0x69, 0x6c, 0x70, 0x74, 0x65, 0x69, 0x72, 0x74, 0x6e, 0x76, 0x65, 0x76, 0x76, 0x69, 0x69, 0x65, 0x70, 0x6e, 0x73, 0x6e, 0x36, 0x76, 0x70, 0x76, 0x6c, 0x6c, 0x70, 0x6e, 0x6e, 0x74, 0x2e, 0x6f, 0x32, 0x74, 0x76, 0x74, 0x40, 0x72, 0x6e, 0x72, 0x74, 0x74, 0x2d, 0x6f, 0x72, 0x73, 0x32, 0x72, 0x32, 0x72, 0x70, 0x65, 0x65, 0x6e, 0x72, 0x70, 0x73, 0x72, 0x69, 0x74, 0x74, 0x6e, 0x72, 0x6c, 0x31, 0x74, 0x77, 0x31, 0x63, 0x63, 0x74, 0x69, 0x72, 0x69, 0x72, 0x70, 0x31, 0x74, 0x72, 0x76, 0x65, 0x72, 0x65, 0x6c, 0x76, 0x6d, 0x72, 0x6c, 0x69, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x69, 0x6c, 0x74, 0x6e, 0x65, 0x69, 0x77, 0x73, 0x70, 0x69, 0x72, 0x2d, 0x65, 0x74, 0x2e, 0x65, 0x65, 0x6d, 0x72, 0x31, 0x2d, 0x72, 0x36, 0x65, 0x2d, 0x69, 0x6d, 0x36, 0x6e, 0x72, 0x6d, 0x6c, 0x72, 0x72, 0x65, 0x65, 0x6e, 0x31, 0x6e, 0x40, 0x72, 0x40, 0x6f, 0x73, 0x6d, 0x36, 0x2e, 0x72, 0x65, 0x36, 0x74, 0x77, 0x65, 0x65, 0x73, 0x36, 0x76, 0x6c, 0x6f, 0x2d, 0x36, 0x6d, 0x36, 0x70, 0x32, 0x74, 0x6d, 0x65, 0x6d, 0x69, 0x65, 0x65, 0x69, 0x76, 0x69, 0x74, 0x2d, 0x63, 0x2d, 0x6e, 0x32, 0x72, 0x63, 0x2d, 0x77, 0x72, 0x74, 0x72, 0x70, 0x6e, 0x76, 0x6f, 0x72, 0x40, 0x65, 0x65, 0x6d, 0x77, 0x2d, 0x2d, 0x74, 0x6e, 0x73, 0x76, 0x65, 0x69, 0x69, 0x72, 0x6f, 0x65, 0x70, 0x69, 0x6d, 0x76, 0x69, 0x65, 0x72, 0x2d, 0x74, 0x2d, 0x69, 0x65, 0x72, 0x69, 0x6f, 0x72, 0x72, 0x69, 0x76, 0x72, 0x77, 0x69, 0x2e, 0x77, 0x69, 0x70, 0x69, 0x6d, 0x36, 0x72, 0x76, 0x65, 0x76, 0x73, 0x6e, 0x72, 0x65, 0x2e, 0x76, 0x2d, 0x76, 0x6f, 0x2d, 0x65, 0x73, 0x72, 0x74, 0x36, 0x2d, 0x6f, 0x70, 0x73, 0x74, 0x74, 0x77, 0x6c, 0x2d, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x72, 0x32, 0x2d, 0x72, 0x69, 0x6d, 0x6e, 0x72, 0x6c, 0x6f, 0x65, 0x36, 0x31, 0x65, 0x65, 0x69, 0x73, 0x31, 0x74, 0x69, 0x69, 0x65, 0x40, 0x69, 0x6e, 0x2d, 0x63, 0x40, 0x31, 0x70, 0x65, 0x6e, 0x2d, 0x69, 0x72, 0x65, 0x65, 0x76, 0x65, 0x70, 0x72, 0x6c, 0x2d, 0x6e, 0x73, 0x69, 0x65, 0x65, 0x6e, 0x2e, 0x63, 0x6c, 0x72, 0x65, 0x2d, 0x2e, 0x6d, 0x72, 0x76, 0x70, 0x69, 0x6d, 0x40, 0x32, 0x77, 0x72, 0x6e, 0x72, 0x6c, 0x36, 0x72, 0x31, 0x2d, 0x73, 0x74, 0x2d, 0x69, 0x63, 0x40, 0x70, 0x32, 0x65, 0x31, 0x69, 0x69, 0x65, 0x72, 0x63, 0x74, 0x72, 0x74, 0x77, 0x6e, 0x69, 0x72, 0x65, 0x76, 0x65, 0x77, 0x69, 0x69, 0x73, 0x6e, 0x77, 0x77, 0x73, 0x6f, 0x69, 0x70, 0x73, 0x2d, 0x65, 0x65, 0x73, 0x65, 0x77, 0x2d, 0x73, 0x72, 0x6d, 0x65, 0x32, 0x6e, 0x73, 0x36, 0x65, 0x72, 0x77, 0x70, 0x65, 0x69, 0x2d, 0x2d, 0x74, 0x6f, 0x6f, 0x36, 0x63, 0x74, 0x72, 0x63, 0x77, 0x69, 0x2e, 0x31, 0x6c, 0x65, 0x77, 0x72, 0x65, 0x76, 0x74, 0x2d, 0x77, 0x2e, 0x76, 0x72, 0x6e, 0x36, 0x70, 0x69, 0x2e, 0x6e, 0x72, 0x77, 0x69, 0x65, 0x74, 0x2d, 0x6e, 0x63, 0x6e, 0x70, 0x2d, 0x6e, 0x2e, 0x6d, 0x69, 0x65, 0x63, 0x65, 0x2d, 0x76, 0x70, 0x73, 0x31, 0x73, 0x2d, 0x76, 0x6e, 0x6e, 0x6c, 0x2d, 0x6c, 0x2d, 0x65, 0x6e, 0x73, 0x6c, 0x65, 0x74, 0x70, 0x65, 0x2d, 0x6e, 0x77, 0x76, 0x40, 0x69, 0x40, 0x63, 0x6f, 0x72, 0x32, 0x6d, 0x31, 0x72, 0x36, 0x69, 0x73, 0x72, 0x70, 0x65, 0x2d, 0x6c, 0x2e, 0x72, 0x74, 0x74, 0x65, 0x69, 0x6f, 0x69, 0x2d, 0x2d, 0x65, 0x6f, 0x65, 0x74, 0x72, 0x69, 0x76, 0x72, 0x72, 0x65, 0x69, 0x76, 0x69, 0x69, 0x6e, 0x31, 0x65, 0x76, 0x72, 0x73, 0x77, 0x72, 0x2d, 0x69, 0x65, 0x69, 0x70, 0x65, 0x6e, 0x6e, 0x65, 0x65, 0x6e, 0x2d, 0x72, 0x76, 0x72, 0x6c, 0x2e, 0x70, 0x76, 0x6e, 0x69, 0x72, 0x70, 0x73, 0x2d, 0x69, 0x74, 0x76, 0x72, 0x70, 0x65, 0x63, 0x72, 0x70, 0x6e, 0x36, 0x6c, 0x74, 0x72, 0x72, 0x72, 0x73, 0x65, 0x40, 0x63, 0x6d, 0x63, 0x32, 0x65, 0x32, 0x69, 0x6e,
        0x77, 0x65, 0x74, 0x72, 0x77, 0x40, 0x69, 0x65, 0x70, 0x31, 0x36, 0x72, 0x73, 0x2d, 0x72, 0x72, 0x32, 0x72, 0x6c, 0x77, 0x6e, 0x6f, 0x77, 0x6c, 0x74, 0x72, 0x2d, 0x6e, 0x65, 0x70, 0x6c, 0x72, 0x6f, 0x69, 0x2d, 0x2d, 0x69, 0x36, 0x69, 0x69, 0x76, 0x69, 0x69, 0x6d, 0x72, 0x73, 0x6f, 0x6d, 0x74, 0x70, 0x76, 0x6d, 0x6d, 0x69, 0x72, 0x70, 0x70, 0x2d, 0x31, 0x63, 0x6c, 0x65, 0x65, 0x6e, 0x2d, 0x77, 0x74, 0x73, 0x6c, 0x72, 0x6e, 0x65, 0x65, 0x2d, 0x6c, 0x69, 0x2d, 0x6e, 0x74, 0x70, 0x72, 0x77, 0x77, 0x65, 0x65, 0x65, 0x2d, 0x76, 0x6e, 0x72, 0x69, 0x69, 0x73, 0x65, 0x74, 0x73, 0x76, 0x72, 0x72, 0x72, 0x69, 0x72, 0x73, 0x72, 0x6f, 0x2e, 0x77, 0x2d, 0x2d, 0x6c, 0x6e, 0x65, 0x65, 0x6d, 0x73, 0x2e, 0x72, 0x65, 0x73, 0x40, 0x69, 0x32, 0x69, 0x32, 0x6e, 0x65, 0x32, 0x65, 0x74, 0x6d, 0x65, 0x74, 0x36, 0x6e, 0x72, 0x32, 0x6e, 0x65, 0x69, 0x32, 0x6f, 0x70, 0x72, 0x72, 0x65, 0x72, 0x72, 0x69, 0x6e, 0x6d, 0x69, 0x70, 0x6c, 0x6c, 0x65, 0x31, 0x72, 0x72, 0x73, 0x72, 0x70, 0x73, 0x72, 0x65, 0x65, 0x6e, 0x76, 0x69, 0x6d, 0x65, 0x6c, 0x65, 0x31, 0x74, 0x74, 0x72, 0x63, 0x2e, 0x69, 0x65, 0x2d, 0x6d, 0x72, 0x70, 0x6e, 0x6c, 0x65, 0x31, 0x73, 0x73, 0x40, 0x74, 0x72, 0x73, 0x2e, 0x74, 0x6e, 0x77, 0x6c, 0x6f, 0x70, 0x77, 0x76, 0x73, 0x72, 0x69, 0x77, 0x69, 0x6e, 0x69, 0x2d, 0x72, 0x70, 0x70, 0x73, 0x2e, 0x76, 0x73, 0x65, 0x72, 0x72, 0x74, 0x2d, 0x72, 0x65, 0x76, 0x69, 0x77, 0x72, 0x65, 0x2d, 0x72, 0x69, 0x36, 0x77, 0x77, 0x77, 0x40, 0x2d, 0x6d, 0x69, 0x74, 0x72, 0x2d, 0x32, 0x6f, 0x76, 0x72, 0x2d, 0x2d, 0x65, 0x2e, 0x2e, 0x72, 0x6e, 0x32, 0x74, 0x6c, 0x6e, 0x6c, 0x2e, 0x6d, 0x2d, 0x6f, 0x65, 0x72, 0x2d, 0x6e, 0x65, 0x65, 0x69, 0x40, 0x69, 0x77, 0x65, 0x6c, 0x2d, 0x69, 0x69, 0x65, 0x72, 0x72, 0x32, 0x40, 0x73, 0x65, 0x36, 0x76, 0x73, 0x72, 0x69, 0x63, 0x77, 0x72, 0x6c, 0x72, 0x6e, 0x74, 0x2d, 0x65, 0x69, 0x72, 0x70, 0x6d, 0x65, 0x6c, 0x73, 0x65, 0x6c, 0x32, 0x2d, 0x73, 0x70, 0x2d, 0x31, 0x72, 0x74, 0x2e, 0x65, 0x74, 0x72, 0x74, 0x72, 0x70, 0x69, 0x40, 0x36, 0x2d, 0x74, 0x72, 0x6c, 0x2d, 0x6e, 0x72, 0x6e, 0x6d, 0x63, 0x76, 0x74, 0x6d, 0x70, 0x32, 0x70, 0x69, 0x69, 0x2d, 0x73, 0x72, 0x74, 0x65, 0x74, 0x74, 0x70, 0x2d, 0x31, 0x6c, 0x77, 0x65, 0x72, 0x70, 0x73, 0x36, 0x6c, 0x72, 0x72, 0x65, 0x65, 0x76, 0x69, 0x2e, 0x6e, 0x72, 0x72, 0x36, 0x65, 0x69, 0x72, 0x69, 0x40, 0x6c, 0x74, 0x6c, 0x72, 0x2d, 0x70, 0x74, 0x76, 0x74, 0x6f, 0x72, 0x31, 0x73, 0x70, 0x65, 0x74, 0x69, 0x6e, 0x69, 0x6c, 0x70, 0x72, 0x65, 0x70, 0x72, 0x73, 0x69, 0x2d, 0x6d, 0x63, 0x2d, 0x72, 0x2d, 0x36, 0x73, 0x6e, 0x2d, 0x6d, 0x69, 0x76, 0x76, 0x6d, 0x74, 0x72, 0x77, 0x74, 0x2e, 0x6d, 0x65, 0x2d, 0x65, 0x6d, 0x2e, 0x6c, 0x73, 0x6e, 0x6f, 0x76, 0x31, 0x74, 0x65, 0x65, 0x31, 0x69, 0x65, 0x32, 0x2d, 0x74, 0x2d, 0x77, 0x77, 0x77, 0x2e, 0x70, 0x65, 0x6e, 0x2d, 0x69, 0x32, 0x72, 0x73, 0x74, 0x65, 0x65, 0x69, 0x73, 0x77, 0x77, 0x2e, 0x6e, 0x72, 0x65, 0x70, 0x76, 0x40, 0x77, 0x65, 0x2d, 0x70, 0x36, 0x2d, 0x74, 0x65, 0x2d, 0x69, 0x74, 0x76, 0x69, 0x6e, 0x65, 0x2d, 0x65, 0x73, 0x31, 0x36, 0x69, 0x31, 0x74, 0x76, 0x65, 0x77, 0x6c, 0x6e, 0x6c, 0x32, 0x6e, 0x70, 0x73, 0x69, 0x69, 0x65, 0x72, 0x2d, 0x6e, 0x2d, 0x65, 0x65, 0x6c, 0x32, 0x77, 0x72, 0x69, 0x70, 0x76, 0x32, 0x65, 0x6c, 0x36, 0x65, 0x69, 0x31, 0x6e, 0x72, 0x6c, 0x6d, 0x65, 0x65, 0x77, 0x6e, 0x2d, 0x32, 0x77, 0x69, 0x65, 0x6d, 0x74, 0x77, 0x40, 0x65, 0x6e, 0x77, 0x73, 0x65, 0x72, 0x6c, 0x40, 0x65, 0x65, 0x72, 0x72, 0x74, 0x6e, 0x6c, 0x6d, 0x73, 0x69, 0x76, 0x72, 0x31, 0x2d, 0x65, 0x36, 0x72, 0x2d, 0x70, 0x69, 0x6e, 0x63, 0x31, 0x2d, 0x69, 0x6e, 0x65, 0x2d, 0x65, 0x2e, 0x77, 0x2d, 0x72, 0x76, 0x63, 0x69, 0x2d, 0x6d, 0x70, 0x2d, 0x6c, 0x69, 0x63, 0x69, 0x77, 0x6e, 0x69, 0x77, 0x36, 0x72, 0x69, 0x72, 0x2e, 0x74, 0x72, 0x6e, 0x65, 0x6f, 0x73, 0x2d, 0x2e, 0x72, 0x63, 0x76, 0x74, 0x36, 0x65, 0x72, 0x65, 0x6d, 0x32, 0x72, 0x70, 0x40, 0x65, 0x74, 0x6e, 0x32, 0x70, 0x2d, 0x31, 0x40, 0x6c, 0x65, 0x6c, 0x76, 0x69, 0x69, 0x76, 0x76, 0x73, 0x31, 0x6e, 0x65, 0x74, 0x65, 0x6d, 0x69, 0x2d, 0x72, 0x74, 0x74, 0x6c, 0x31, 0x74, 0x6e, 0x6e, 0x65, 0x77, 0x36, 0x69, 0x69, 0x72, 0x6e, 0x2d, 0x2d, 0x2d, 0x72, 0x73, 0x76, 0x72, 0x72, 0x65, 0x72, 0x65, 0x72, 0x2d, 0x6c, 0x76, 0x77, 0x63, 0x77, 0x72, 0x6d, 0x72, 0x2e, 0x65, 0x73, 0x32, 0x72, 0x36, 0x77, 0x72, 0x72, 0x6d, 0x74, 0x2d, 0x72, 0x2e, 0x73, 0x73, 0x65, 0x77, 0x6e, 0x65, 0x69, 0x65, 0x2d, 0x65, 0x77, 0x6f, 0x74, 0x72, 0x32, 0x40, 0x6e, 0x72, 0x69, 0x6e, 0x32, 0x70, 0x73, 0x72, 0x40, 0x2d, 0x65, 0x69, 0x65, 0x77, 0x65, 0x70, 0x40, 0x36, 0x72, 0x6c, 0x6d, 0x73, 0x69, 0x72, 0x72, 0x74, 0x36, 0x6c, 0x76, 0x65, 0x76, 0x2d, 0x74, 0x6c, 0x72, 0x72, 0x74, 0x6e, 0x73, 0x74, 0x69, 0x72, 0x6d, 0x40, 0x2d, 0x6e, 0x70, 0x73, 0x2d, 0x6d, 0x72, 0x72, 0x70, 0x65, 0x65, 0x36, 0x6e, 0x77, 0x2d, 0x69, 0x2d, 0x32, 0x72, 0x6d, 0x72, 0x6c, 0x32, 0x6c, 0x73, 0x6d, 0x65, 0x36, 0x69, 0x69, 0x72, 0x77, 0x74, 0x6f, 0x72, 0x6d, 0x6d, 0x69, 0x65, 0x73, 0x63, 0x65, 0x74, 0x74, 0x72, 0x65, 0x72, 0x2e, 0x6e, 0x73, 0x65, 0x76, 0x6c, 0x76, 0x77, 0x72, 0x6e, 0x6c, 0x32, 0x2d, 0x73, 0x65, 0x73, 0x2e, 0x76, 0x72, 0x65, 0x2d, 0x72, 0x77, 0x2d, 0x77, 0x70, 0x65, 0x6c, 0x72, 0x6e, 0x2e, 0x31, 0x73, 0x2e, 0x72, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x6d, 0x32, 0x70, 0x76, 0x76, 0x31, 0x76, 0x77, 0x65, 0x6e, 0x73, 0x63, 0x2e, 0x2d, 0x69, 0x6e, 0x69, 0x77, 0x6e, 0x65, 0x6d, 0x2d, 0x72, 0x6e, 0x74, 0x6e, 0x40, 0x73, 0x2d, 0x74, 0x74, 0x65, 0x72, 0x2d, 0x2d, 0x69, 0x73, 0x70,
        0x69, 0x6c, 0x72, 0x76, 0x6d, 0x74, 0x6e, 0x73, 0x74, 0x72, 0x65, 0x63, 0x69, 0x65, 0x65, 0x72, 0x6f, 0x6e, 0x72, 0x72, 0x6c, 0x6e, 0x6e, 0x65, 0x6d, 0x74, 0x6c, 0x74, 0x65, 0x69, 0x2d, 0x6f, 0x69, 0x2e, 0x6e, 0x63, 0x65, 0x6c, 0x40, 0x70, 0x2d, 0x2d, 0x74, 0x73, 0x74, 0x40, 0x72, 0x74, 0x6c, 0x72, 0x6e, 0x6f, 0x73, 0x65, 0x74, 0x6d, 0x69, 0x32, 0x72, 0x65, 0x77, 0x6e, 0x76, 0x74, 0x73, 0x2d, 0x72, 0x6e, 0x69, 0x73, 0x40, 0x36, 0x2d, 0x6d, 0x2e, 0x65, 0x6d, 0x40, 0x69, 0x72, 0x72, 0x70, 0x65, 0x72, 0x76, 0x6c, 0x65, 0x76, 0x72, 0x65, 0x69, 0x65, 0x69, 0x6e, 0x72, 0x2d, 0x63, 0x72, 0x69, 0x6e, 0x72, 0x69, 0x6e, 0x69, 0x70, 0x6e, 0x2d, 0x69, 0x6c, 0x72, 0x2d, 0x65, 0x2d, 0x72, 0x6f, 0x65, 0x6e, 0x76, 0x6e, 0x40, 0x2d, 0x65, 0x72, 0x72, 0x6f, 0x6f, 0x72, 0x6c, 0x65, 0x74, 0x73, 0x72, 0x70, 0x77, 0x69, 0x69, 0x6d, 0x6c, 0x6d, 0x6e, 0x2d, 0x65, 0x65, 0x65, 0x74, 0x6c, 0x2d, 0x74, 0x6f, 0x2d, 0x74, 0x70, 0x72, 0x6e, 0x73, 0x72, 0x69, 0x72, 0x2e, 0x6d, 0x69, 0x65, 0x65, 0x32, 0x70, 0x6c, 0x6c, 0x65, 0x77, 0x2d, 0x72, 0x6f, 0x70, 0x76, 0x65, 0x2d, 0x72, 0x69, 0x6d, 0x72, 0x36, 0x40, 0x6d, 0x72, 0x6c, 0x6d, 0x77, 0x6c, 0x6e, 0x69, 0x72, 0x6d, 0x76, 0x73, 0x2e, 0x73, 0x72, 0x77, 0x73, 0x76, 0x2d, 0x73, 0x76, 0x6d, 0x76, 0x65, 0x69, 0x76, 0x63, 0x65, 0x72, 0x31, 0x72, 0x69, 0x76, 0x72, 0x65, 0x65, 0x2d, 0x73, 0x6d, 0x31, 0x72, 0x6e, 0x72, 0x2d, 0x2d, 0x36, 0x72, 0x73, 0x77, 0x2d, 0x77, 0x36, 0x76, 0x72, 0x6d, 0x65, 0x2d, 0x72, 0x70, 0x2d, 0x74, 0x32, 0x6c, 0x63, 0x6d, 0x6f, 0x6e, 0x2e, 0x2d, 0x69, 0x65, 0x73, 0x6d, 0x65, 0x73, 0x6e, 0x6d, 0x6c, 0x65, 0x6e, 0x72, 0x72, 0x72, 0x32, 0x70, 0x65, 0x73, 0x6c, 0x6d, 0x70, 0x6d, 0x72, 0x6f, 0x65, 0x6c, 0x76, 0x73, 0x63, 0x73, 0x65, 0x6c, 0x2d, 0x6e, 0x72, 0x65, 0x65, 0x72, 0x2d, 0x70, 0x6d, 0x69, 0x69, 0x65, 0x2d, 0x6c, 0x72, 0x69, 0x6c, 0x2d, 0x74, 0x65, 0x65, 0x69, 0x31, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x74, 0x73, 0x65, 0x32, 0x2d, 0x6f, 0x2d, 0x70, 0x72, 0x6f, 0x65, 0x69, 0x73, 0x32, 0x6d, 0x65, 0x2d, 0x65, 0x74, 0x6d, 0x6d, 0x73, 0x76, 0x6c, 0x69, 0x65, 0x2d, 0x73, 0x74, 0x65, 0x65, 0x72, 0x72, 0x74, 0x31, 0x2d, 0x76, 0x73, 0x2e, 0x2d, 0x2d, 0x72, 0x76, 0x77, 0x65, 0x72, 0x72, 0x40, 0x6e, 0x6c, 0x6d, 0x72, 0x74, 0x73, 0x72, 0x72, 0x65, 0x65, 0x2d, 0x6f, 0x74, 0x70, 0x63, 0xb8, 0xa1, 0x11, 0x6e, 0xd7, 0x74, 0x16, 0x7f, 0xb4, 0xba, 0x40, 0x93, 0x98, 0x00, 0x71, 0xcc, 0x42, 0xa7, 0x2f, 0x28, 0x69, 0xe7, 0x31, 0x48, 0x22, 0xa0, 0xe1, 0x45, 0xe3, 0xf7, 0x7f, 0x3a
    };
    uint8_t c2[] = {
        0x5b, 0x52, 0xf1, 0x2d, 0x94, 0xcb, 0xb0, 0x86, 0xd8, 0xd3, 0xe3, 0x20, 0x88, 0x47, 0xcf, 0x5a, 0x49, 0xd2, 0x11, 0x30, 0x92, 0x17, 0x8d, 0xf4, 0x99, 0xf7, 0x6c, 0x8a, 0xbc, 0xe7, 0x5c, 0x58, 0x6a, 0x65, 0xed, 0x81, 0xdc, 0xdd, 0xcf, 0x83, 0xcd, 0xa4, 0xed, 0xa2, 0x5e, 0x63, 0xd9, 0x98, 0xf6, 0x2e, 0x15, 0x76, 0x9a, 0xc8, 0x8c, 0x42, 0x54, 0x44, 0xf4, 0x47, 0xf5, 0x96, 0xc9, 0x6e, 0x23, 0x09, 0x1a, 0x0d, 0xe3, 0x04, 0xe6, 0xed, 0x48, 0x49, 0x62, 0x31, 0xe8, 0x36, 0x04, 0xed, 0xb9, 0xe7, 0xa6, 0x35, 0x4d, 0xcd, 0xe3, 0xfa, 0xa0, 0xc8, 0x34, 0xbd, 0x62, 0x7b, 0xbc, 0xbe, 0x1c, 0x5b, 0x69, 0x1f, 0x9c, 0x30, 0x20, 0x48, 0x52, 0xd1, 0xb6, 0x5e, 0xa2, 0x6e, 0x06, 0x94, 0x72, 0x10, 0x56, 0x7c, 0x94, 0xa5, 0xc0, 0xaa, 0xea, 0x48, 0x61, 0x03, 0x14, 0x94, 0x09, 0x77, 0xd9, 0xa7, 0xfe, 0x78, 0x17, 0x95, 0x4f, 0x7e, 0xb0, 0x32, 0x63, 0x02, 0x17, 0x47, 0x1e, 0x7d, 0xb2, 0x7d, 0xb5, 0xcb, 0x9f, 0x61, 0x65, 0xed, 0x03, 0xd2, 0xdb, 0xd1, 0xb3, 0xd6, 0x1a, 0xf5, 0x67, 0x0b, 0x8b, 0x6b, 0x44, 0xf2, 0x62, 0x42, 0xc2, 0x4d, 0xe1, 0x5c, 0xfe, 0xc6, 0x19, 0x2b, 0xfb, 0x03, 0x0f, 0x1b, 0x89, 0x08, 0x86, 0x40, 0xca, 0x45, 0x15, 0xda, 0x65, 0xcc, 0x73, 0x00, 0x49, 0x4e, 0x48, 0x21, 0x25, 0xc6, 0xde, 0x26, 0x21, 0x1d, 0xea, 0x3c, 0x11, 0xac, 0xef, 0x34, 0x4c, 0x96, 0xcc, 0x5e, 0x26, 0xf3, 0xcd, 0x70, 0x0d, 0x62, 0xea, 0x09, 0x35, 0x2b, 0x1e, 0x60, 0xe4, 0x76, 0xd3, 0x65, 0x01, 0x8c, 0xab, 0xd4, 0x89, 0xad, 0x81, 0x9d, 0x04, 0x01, 0xd5, 0x55, 0x3c, 0xcb, 0x32, 0xe1, 0xb5, 0xd4, 0xda, 0xb4, 0xa9, 0x01, 0xb2, 0x10, 0xc7, 0xb1, 0xa9, 0x54, 0x66, 0x1d, 0xcc, 0xff, 0x54, 0x0b, 0x84, 0x37, 0xe0, 0x3a, 0xa5, 0x68, 0x80, 0x87, 0xbc, 0x3c, 0x0f, 0xda, 0x7e, 0x3c, 0x23, 0xfc, 0xd8, 0xc5, 0x52, 0xf7, 0x22, 0x12, 0x05, 0x9c, 0x68, 0x39, 0xb1, 0xed, 0x26, 0x24, 0x2b, 0x7e, 0x0b, 0xaf, 0x9e, 0x97, 0x45, 0x7b, 0xa9, 0xbc, 0x48, 0x0e, 0x66, 0x93, 0x32, 0x0d, 0x6b, 0xd6, 0xf0, 0x4f, 0x54, 0x18, 0xcd, 0xc9, 0x8c, 0xce, 0xc4, 0xa2, 0xff, 0x1e, 0x69, 0x17, 0x7e, 0xf4, 0x99, 0x09, 0x68, 0xa1, 0x9e, 0x1f, 0xbf, 0x90, 0xdc, 0x77, 0x5d, 0x50, 0x2b, 0x0e, 0xff, 0x96, 0xdc, 0x21, 0x2e, 0x74, 0x22, 0x28, 0x88, 0xa0, 0x00, 0x32, 0x15, 0xb0, 0xfd, 0xb1, 0xc9, 0x75, 0xb3, 0x3c, 0xbd, 0x89, 0xc5, 0xa4, 0x48, 0x17, 0xa9, 0xc9, 0x50, 0x61, 0x0c, 0x35, 0x31, 0x55, 0x11, 0xe3, 0x23, 0xe9, 0x3e, 0x78, 0x25, 0xdc, 0x50, 0xe8, 0x23, 0x5f, 0xb7, 0x3f, 0xc7, 0xae, 0xf0, 0x82, 0x35, 0x46, 0x34, 0x63, 0xcc, 0x5d, 0x96, 0xb8, 0x6a, 0x7a, 0x7f, 0x54, 0x27, 0x1a, 0xa4, 0x63, 0xdd, 0xb0, 0xb6, 0x17, 0x08, 0xa1, 0x2e, 0x95, 0x9e, 0xd4, 0x9b, 0x71, 0x83, 0x81, 0x6c, 0xea, 0xab, 0x00, 0x2e, 0xca, 0x60, 0xc1, 0x4b, 0x83, 0xa7, 0xab, 0x47, 0xe8, 0x1b, 0x5a, 0x78, 0x4f, 0xec, 0xbd, 0x62, 0x94, 0x25, 0x75, 0x2e, 0x64, 0xe7, 0x70, 0x13, 0xac, 0xe9, 0x89, 0x4f, 0x1e, 0x79, 0xbc, 0x15, 0x0c, 0x8d, 0x40, 0xe8, 0x16, 0x31, 0x7c, 0xb8, 0xa5, 0xd7, 0x21, 0x39, 0x93, 0x9b, 0xe6, 0x05, 0x81, 0xb6, 0x20, 0xa8, 0x5d, 0x73, 0x58, 0x8b, 0x66, 0x92, 0xac, 0x23, 0xa0, 0xf4, 0x8c, 0xab, 0x58, 0xae, 0xb6, 0x9c, 0x3c, 0x4d, 0x77, 0x5f, 0xae, 0xe2, 0x57, 0x89, 0x8f, 0xe4, 0x68, 0x81, 0x24, 0x7d, 0x3b, 0x99, 0x46, 0x9f, 0x7b, 0x9d, 0xa6, 0xdd, 0x99, 0xcf, 0xc1, 0x79, 0x04, 0x95, 0xce, 0x96, 0x7a, 0xd9, 0xb5, 0x6e, 0xcf, 0xd1, 0x72, 0x18, 0x97, 0x76, 0xe2, 0xb7, 0x38, 0x1e, 0x24, 0x0b, 0x09, 0x00, 0x8b, 0x28, 0x5d, 0xf8, 0xd0, 0x50, 0x7f, 0xeb, 0x3b, 0x37, 0x61, 0x0b, 0xd3, 0xff, 0x65, 0x7d, 0x88, 0x1e, 0x1d, 0xbb, 0x6c, 0xf5, 0xf8, 0xf3, 0x2b, 0x51, 0xd9, 0x6d, 0xc9, 0xbe, 0xbe, 0xd1, 0x94, 0x0e, 0x58, 0x2a, 0x0a, 0xe4, 0xf8, 0x28, 0x26, 0xc3, 0x74, 0x87, 0xd3, 0x81, 0x48, 0x6e, 0x9b, 0xd5, 0xa1, 0x60, 0x87, 0xfc, 0x1b, 0x06, 0x33, 0x0d, 0x87, 0xfa, 0x9b, 0xf9, 0x73, 0x6b, 0x0c, 0xdf, 0xea, 0xee, 0x32, 0x78, 0xe0, 0xf8, 0x18, 0x3f, 0xc3, 0x3b, 0x12, 0x88, 0x0b, 0xb2, 0x4a, 0x52, 0x64, 0x4e, 0x58, 0x54, 0x82, 0x52, 0x61, 0x54, 0x28, 0x1b, 0xf7, 0x99, 0x06, 0xa2, 0xad, 0x04, 0x19, 0x9f, 0x2e, 0x34, 0xe6, 0xf0, 0xee, 0xeb, 0x93, 0x9a, 0x9c, 0x73, 0x86, 0x23, 0x6d, 0x5d, 0xae, 0x64, 0xec, 0x6f, 0xf9, 0x7c, 0xc7, 0x46, 0x96, 0xdb, 0x44, 0xf4, 0xab, 0xc9, 0x67, 0x61, 0xb8, 0xec, 0xf0, 0x99, 0xe0, 0x4d, 0x45, 0xed, 0xa3, 0x1c, 0xe9, 0x68, 0x31, 0x85, 0xa5, 0xa1, 0xba, 0x08, 0xdb, 0x3f, 0x84, 0x75, 0x70, 0x24, 0xcd, 0x49, 0xd4, 0x07, 0xa8, 0xaa, 0x52, 0xd9, 0x55, 0x68, 0x8f, 0x78, 0xd2, 0x5d, 0x46, 0x23, 0x60, 0x76, 0xe1, 0x22, 0xdc, 0x2a, 0xeb, 0xac, 0xbc, 0xeb, 0xd6, 0x4c, 0x0f, 0xb5, 0xcb, 0x47, 0xce, 0x43, 0x59, 0x1d, 0x3e, 0xfc, 0x7f, 0x7c, 0x93, 0x9e, 0xef, 0xcd, 0x79, 0x5c, 0x08, 0x8e, 0xeb, 0xa8, 0x98, 0x3e, 0x95, 0xd1, 0x36, 0x42, 0x57, 0xfd, 0x6d, 0xdc, 0xe0, 0xa3, 0x3f, 0x46, 0x32, 0xb7, 0xff, 0x00, 0x4f, 0x7b, 0x23, 0x4d, 0xd0, 0xe5, 0xdd, 0x40, 0xab, 0xb2, 0xcb, 0x45, 0x92, 0x76, 0x7c, 0x5b, 0x98, 0xc7, 0xc0, 0x54, 0x34, 0x94, 0x8e, 0xbb, 0x28, 0xcf, 0xba, 0xd9, 0xa0, 0xe6, 0xf3, 0x65, 0x61, 0xd7, 0x10, 0xd3, 0xeb, 0xce, 0x21, 0x6a, 0xca, 0x61, 0xe7, 0x81, 0x15, 0x18, 0x4e, 0x71, 0xb0, 0x99, 0x62, 0xd9, 0xeb, 0xd0, 0x8b, 0xe9, 0xdf, 0x6a, 0x6d, 0x59, 0x0b, 0x45, 0x93, 0x38, 0xfe, 0xe6, 0x6a, 0xd1, 0x5f, 0xb6, 0xe9, 0x86, 0x01, 0x38, 0xab, 0x59, 0x5c, 0xd7, 0xb7, 0xfa, 0x81, 0x8a, 0xbe, 0xdc, 0xeb, 0x50, 0x7d, 0x81, 0xfa, 0x1b, 0x8f, 0xce, 0x53, 0x38, 0xe4, 0x8a, 0x82, 0xbe, 0x7d, 0xdc, 0xd8, 0x57, 0x5a, 0x48, 0xa3, 0x38, 0x74, 0x8a, 0xac, 0xf2, 0xfd, 0xbf, 0xcc, 0xd8, 0x08, 0x4d, 0x3e, 0xae, 0xa9, 0x00, 0x66, 0x06, 0xcb, 0xf3,
        0x50, 0xcc, 0x52, 0xc7, 0x4b, 0x16, 0x33, 0xa5, 0xde, 0x20, 0xed, 0x6a, 0xa7, 0x58, 0x5e, 0x4e, 0x7e, 0x29, 0xab, 0xb9, 0x65, 0x9d, 0x17, 0xe0, 0x1e, 0x79, 0x77, 0xf6, 0x1e, 0xa0, 0xcb, 0x0c, 0xf7, 0xc0, 0xe4, 0xf6, 0x3b, 0x60, 0x81, 0xfe, 0xed, 0xd9, 0x42, 0xa9, 0x61, 0x9d, 0xa8, 0xd7, 0xe8, 0xaa, 0x97, 0xad, 0xbb, 0xba, 0x13, 0x6e, 0x05, 0xa5, 0xce, 0x7a, 0x65, 0x6f, 0x55, 0xe3, 0xcf, 0xbc, 0x67, 0x14, 0x64, 0x57, 0x9c, 0x46, 0x14, 0xd6, 0x1d, 0x39, 0x1c, 0xd3, 0xe8, 0x98, 0x20, 0x5a, 0x1a, 0x05, 0x3b, 0x27, 0xd5, 0x84, 0xca, 0xd4, 0x0b, 0xc4, 0x1e, 0xd8, 0x46, 0x29, 0x48, 0x95, 0xdb, 0xe5, 0x58, 0x8a, 0x51, 0xc7, 0x74, 0x7f, 0x53, 0xa8, 0xbb, 0x58, 0xc0, 0x5b, 0xe1, 0xa7, 0x27, 0x36, 0x6c, 0xa6, 0x70, 0xec, 0x88, 0xcd, 0x9a, 0x70, 0xe1, 0xa0, 0xc7, 0xdd, 0x60, 0x71, 0xf4, 0x2a, 0x51, 0x98, 0x8e, 0xab, 0xb8, 0x13, 0x03, 0x48, 0x5f, 0x44, 0xf8, 0x88, 0xd9, 0x7d, 0xd3, 0xf1, 0x5f, 0xc4, 0x2b, 0x44, 0x15, 0x57, 0x31, 0xa4, 0xa1, 0xdb, 0x6d, 0x2a, 0x5a, 0x5a, 0xf7, 0xde, 0xd5, 0x23, 0x38, 0x00, 0xe5, 0x5c, 0x55, 0xe7, 0x37, 0x9c, 0xcb, 0x8b, 0xc0, 0x33, 0x42, 0x68, 0x23, 0x84, 0x7d, 0x89, 0x9d, 0xae, 0x59, 0x18, 0xae, 0xea, 0x46, 0x3f, 0xac, 0x57, 0x0d, 0x5d, 0x49, 0x14, 0x50, 0xe5, 0x70, 0x17, 0x73, 0x09, 0x11, 0x93, 0x6b, 0x02, 0x22, 0xb7, 0x63, 0xc9, 0xe6, 0xa4, 0xe3, 0xb1, 0xf7, 0xa6, 0x58, 0x8d, 0x14, 0xa1, 0xda, 0x6a, 0xb9, 0x38, 0xf9, 0x20, 0x45, 0x8c, 0xe6, 0x32, 0x23, 0x9d, 0x5f, 0xba, 0xcb, 0xb4, 0x95, 0xf9, 0xa9, 0x5c, 0x60, 0x03, 0x5a, 0x8c, 0xa7, 0xb9, 0x65, 0xa8, 0x84, 0x38, 0xc0, 0x25, 0xe6, 0xa7, 0xc0, 0x3b, 0xbc, 0x11, 0xed, 0x0e, 0x9a, 0x6f, 0xfe, 0x61, 0x79, 0x86, 0x92, 0x3a, 0xce, 0xe0, 0xb7, 0x70, 0xad, 0xe0, 0xcc, 0x88, 0x47, 0xd9, 0x2a, 0x3d, 0x41, 0x06, 0x77, 0x41, 0xbe, 0x3f, 0x55, 0x31, 0x54, 0x10, 0x14, 0x5b, 0xdf, 0x88, 0xb2, 0x9f, 0xff, 0x11, 0xb8, 0x11, 0xdc, 0x5e, 0x64, 0xf9, 0x97, 0x8a, 0x26, 0x6a, 0x44, 0xb4, 0x83, 0x83, 0x9b, 0x81, 0xaa, 0xfd, 0xb5, 0x8b, 0x16, 0x18, 0x2e, 0x5c, 0xe4, 0x5b, 0x8f, 0xdd, 0x7c, 0x1f, 0x33, 0x2f, 0xef, 0x57, 0x8c, 0x6a, 0x3f, 0x3c, 0x19, 0x5e, 0x73, 0x64, 0xc5, 0xaf, 0x1d, 0xa1, 0xb4, 0x11, 0xee, 0x6b, 0x7e, 0x66, 0xfb, 0xaa, 0x03, 0x17, 0xe4, 0xc9, 0x90, 0x4b, 0xf2, 0x50, 0x55, 0x71, 0xad, 0x31, 0x71, 0x49, 0xd7, 0x80, 0xd1, 0xa5, 0x9f, 0x6d, 0x71, 0x28, 0x2b, 0x65, 0xcf, 0x8d, 0xb1, 0x2a, 0x33, 0xdc, 0x93, 0xff, 0x86, 0xd7, 0xa6, 0xd0, 0x46, 0x66, 0x32, 0x3d, 0x18, 0x8c, 0xd3, 0xda, 0xf6, 0x1b, 0xa0, 0x2d, 0x29, 0xfd, 0x8d, 0x57, 0x2c, 0x82, 0xed, 0x38, 0x4a, 0x6f, 0xc4, 0x3c, 0x9a, 0x61, 0xcb, 0xe5, 0xcf, 0xd3, 0x83, 0xa1, 0x91, 0x93, 0x0d, 0x75, 0xfd, 0x4e, 0x2c, 0x83, 0xa0, 0x85, 0x27, 0x13, 0x5a, 0x24, 0xbd, 0x08, 0x1e, 0xe9, 0xab, 0x92, 0x41, 0xc2, 0x3a, 0xa0, 0xe1, 0xfd, 0x00, 0xb9, 0xf8, 0xca, 0x0b, 0x1a, 0x8e, 0xf6, 0x27, 0x9f, 0x5a, 0xf0, 0x23, 0x07, 0xc8, 0xbf, 0xf6, 0x74, 0xe7, 0xf8, 0x67, 0xfc, 0x28, 0x4e, 0x6a, 0x6c, 0xc6, 0x83, 0xe3, 0xf0, 0x01, 0xe0, 0x0f, 0x2d, 0xdf, 0x9e, 0x4b, 0x8b, 0x06, 0x15, 0x4c, 0x9f, 0xdf, 0x55, 0x14, 0x44, 0xde, 0x34, 0x35, 0x5a, 0xcb, 0xe5, 0xa7, 0xb5, 0x7e, 0x00, 0x31, 0x98, 0x5f, 0x51, 0x11, 0x37, 0xe1, 0xd2, 0x99, 0x8f, 0x70, 0x13, 0x40, 0xa0, 0xbe, 0xf8, 0xde, 0xac, 0x37, 0x06, 0xb6, 0x26, 0xf3, 0xb1, 0x97, 0x0b, 0x85, 0x68, 0x09, 0xa4, 0xc8, 0x34, 0x0a, 0x41, 0x6e, 0xac, 0x1a, 0x5b, 0xe0, 0x91, 0x6f, 0xa3, 0x0a, 0xf6, 0x05, 0x37, 0x32, 0xe1, 0x8e, 0xd8, 0xed, 0x55, 0xa3, 0x54, 0x3f, 0x62, 0x95, 0x82, 0xcf, 0x0a, 0x19, 0xb4, 0x9f, 0x04, 0xcc, 0x86, 0x7e, 0xf1, 0xe5, 0x8b, 0x67, 0x73, 0xa2, 0x46, 0x4e, 0xf2, 0x98, 0x94, 0xb5, 0xeb, 0xa5, 0xbd, 0xcb, 0x66, 0x82, 0xe9, 0x87, 0xe9, 0xe3, 0x50, 0x55, 0x4b, 0xd6, 0x67, 0x30, 0xe1, 0x7c, 0x15, 0x77, 0x29, 0xfd, 0x85, 0x67, 0x5a, 0xc4, 0xd5, 0x69, 0xfa, 0xc7, 0x66, 0x66, 0x49, 0xf7, 0x5a, 0xcd, 0xd1, 0x81, 0x5c, 0x74, 0x8d, 0xbf, 0xc5, 0xc2, 0xff, 0x4d, 0x90, 0xe8, 0x8e, 0x05, 0x00, 0xff, 0x7a, 0xd7, 0xb2, 0x7a, 0xad, 0x8b, 0xd6, 0x4b, 0x52, 0x09, 0x50, 0x4b
    };

    SrsHandshakeBytes bytes;
    if (true) {
        ASSERT_EQ(ERROR_SUCCESS, bytes.create_c0c1());
        memcpy(bytes.c0c1, c0c1, 1537);
        ASSERT_EQ(ERROR_SUCCESS, bytes.create_s0s1s2());
        memcpy(bytes.s0s1s2, s0s1s2, 3073);
        ASSERT_EQ(ERROR_SUCCESS, bytes.create_c2());
        memcpy(bytes.c2, c2, 1536);
    }
    
    SrsHandshakeBytes* hs_bytes = &bytes;
    if (true) {
        bool is_valid;
        
        c1s1 c1;
        ASSERT_EQ(ERROR_SUCCESS, c1.parse(hs_bytes->c0c1 + 1, 1536, srs_schema0));
        ASSERT_EQ(ERROR_SUCCESS, c1.c1_validate_digest(is_valid));
        ASSERT_TRUE(is_valid);
        
        c1s1 s1;
        ASSERT_EQ(ERROR_SUCCESS, s1.parse(hs_bytes->s0s1s2 + 1, 1536, c1.schema()));
        ASSERT_EQ(ERROR_SUCCESS, s1.s1_validate_digest(is_valid));
        ASSERT_TRUE(is_valid);
        
        c2s2 c2;
        c2.parse(hs_bytes->c2, 1536);
        ASSERT_EQ(ERROR_SUCCESS, c2.c2_validate(&s1, is_valid));
        ASSERT_TRUE(is_valid);
        
        c2s2 s2;
        s2.parse(hs_bytes->s0s1s2 + 1 + 1536, 1536);
        ASSERT_EQ(ERROR_SUCCESS, s2.s2_validate(&c1, is_valid));
        ASSERT_TRUE(is_valid);
    }

    if (true) {
        MockBufferIO io;
        io.append(s0s1s2, 3073);

        SrsRtmpClient r(&io);
        HELPER_EXPECT_SUCCESS(r.handshake());
    }

    if (true) {
        MockBufferIO io;
        io.append(s0s1s2, 3073);

        SrsRtmpClient r(&io);
        HELPER_EXPECT_SUCCESS(r.complex_handshake());
    }

    if (true) {
        MockBufferIO io;
        io.append(c0c1, 1537);
        io.append(c2, 1536);

        SrsRtmpServer r(&io);
        HELPER_EXPECT_SUCCESS(r.handshake());
    }
}

VOID TEST(ProtocolHandshakeTest, SimpleHandshake)
{
    srs_error_t err;

    uint8_t c0c1[] = {
        0x03, 0x01, 0x14, 0xf7, 0x4e, 0x80, 0x00, 0x07, 0x02, 0xac, 0x14, 0x98, 0x57, 0x0a, 0x07, 0x58, 0x44, 0x96, 0x47, 0xb5, 0x9a, 0x73, 0xf6, 0x07, 0x0f, 0x49, 0x0d, 0x72, 0xb8, 0x16, 0xbb, 0xb2, 0xb7, 0x61, 0x17, 0x79, 0xa0, 0xe9, 0x98, 0xca, 0xb2, 0x86, 0x64, 0x5f, 0x65, 0x3e, 0xfc, 0x4d, 0xc0, 0x0e, 0x4c, 0xfa, 0x91, 0xc7, 0x0f, 0x2e, 0x57, 0x31, 0x4b, 0x96, 0xef, 0xc9, 0x81, 0x02, 0x00, 0x54, 0x25, 0x2b, 0xb2, 0x0d, 0x7c, 0xee, 0xba, 0xdb, 0xe4, 0x06, 0x78, 0xcd, 0x70, 0x2c, 0x54, 0x5a, 0x3a, 0x03, 0x13, 0x2e, 0xe7, 0x4b, 0x87, 0x40, 0x77, 0x0b, 0x9f, 0xd2, 0xab, 0x32, 0x07, 0x6f, 0x1e, 0x75, 0x74, 0xe9, 0xc7, 0x44, 0xd9, 0x76, 0x53, 0xba, 0xe2, 0x52, 0xfa, 0xcc, 0xef, 0x34, 0xd5, 0x14, 0x61, 0xac, 0xcc, 0x63, 0xfd, 0x2b, 0x2d, 0xb3, 0xb8, 0xdd, 0x8a, 0x51, 0x9a, 0x2d, 0x0e, 0xfa, 0x84, 0x25, 0x55, 0xb2, 0xb7, 0x94, 0x54, 0x68, 0xfb, 0x94, 0xdf, 0xd8, 0xeb, 0x43, 0xd0, 0x11, 0x70, 0x8f, 0xf5, 0x48, 0xfc, 0x69, 0x4d, 0x5b, 0xc6, 0x53, 0x8a, 0x22, 0xea, 0x62, 0x84, 0x89, 0x6b, 0xfe, 0x4e, 0xab, 0x51, 0x98, 0xf4, 0x4f, 0xae, 0xf8, 0xdf, 0xac, 0x43, 0xed, 0x5a, 0x04, 0x97, 0xc4, 0xbe, 0x44, 0x5b, 0x99, 0x20, 0x68, 0x67, 0x0f, 0xe3, 0xfa, 0x4c, 0x9d, 0xe7, 0x0b, 0x3f, 0x80, 0x7c, 0x4c, 0x35, 0xf6, 0xdd, 0x20, 0x05, 0xfd, 0x0f, 0x39, 0xb7, 0x36, 0x45, 0x4c, 0xb7, 0x62, 0x92, 0x35, 0x2a, 0xcd, 0xb9, 0x49, 0xea, 0x12, 0x0b, 0x5f, 0x39, 0xae, 0x3b, 0x49, 0x29, 0xe6, 0x30, 0xc7, 0x7c, 0x77, 0xaf, 0x00, 0x43, 0x4d, 0x06, 0x45, 0x72, 0x73, 0x25, 0x71, 0x5e, 0x35, 0x04, 0xbd, 0xe9, 0x48, 0x23, 0x64, 0x4d, 0x15, 0x0b, 0xc5, 0x3f, 0x6e, 0x3a, 0xd5, 0xd5, 0xa6, 0xae, 0x3b, 0x4c, 0x66, 0x6a, 0x70, 0x8b, 0xf3, 0x6a, 0x43, 0xc4, 0xb9, 0xbd, 0xa0, 0x09, 0x72, 0xbc, 0xce, 0x7a, 0xea, 0x49, 0xf2, 0x86, 0xa7, 0xd8, 0x4a, 0x87, 0x28, 0xca, 0x2c, 0x53, 0xee, 0x96, 0x0b, 0xbe, 0x15, 0x14, 0xa8, 0x00, 0xca, 0x76, 0x08, 0x4d, 0x0f, 0xef, 0x78, 0x4b, 0xf6, 0x47, 0x60, 0xfc, 0x16, 0x00, 0x7c, 0x6b, 0x49, 0x39, 0x64, 0x36, 0xee, 0x45, 0x3a, 0x9a, 0xa5, 0xbf, 0xfb, 0x7b, 0xe7, 0xcf, 0x42, 0x82, 0x48, 0x1b, 0x30, 0xfe, 0x0d, 0xba, 0x10, 0xb8, 0xe1, 0x40, 0xcc, 0x6f, 0x36, 0x1c, 0x94, 0x5d, 0x50, 0x9e, 0x21, 0x08, 0xc9, 0xd5, 0xb0, 0x32, 0x51, 0x6a, 0x8f, 0xfa, 0x57, 0x8d, 0x45, 0xd7, 0xd2, 0xd0, 0xd6, 0x6c, 0x78, 0x95, 0xe9, 0xe1, 0x20, 0x97, 0x1a, 0x43, 0x40, 0xa3, 0xb5, 0xcc, 0x4b, 0x12, 0x84, 0x1e, 0x0e, 0xd3, 0x32, 0xca, 0x99, 0xc3, 0x2b, 0x78, 0x17, 0x24, 0x6b, 0xc7, 0xbc, 0x9d, 0x05, 0xc6, 0xaf, 0x8f, 0x19, 0x75, 0x3c, 0x08, 0xa6, 0x08, 0x26, 0x5b, 0xf4, 0x10, 0x40, 0xaa, 0x6a, 0x7e, 0xb9, 0xde, 0x0b, 0x23, 0x3f, 0x53, 0x5a, 0x20, 0x13, 0x62, 0xec, 0x53, 0x86, 0x81, 0x1f, 0xf6, 0x8e, 0xe3, 0xd1, 0xaa, 0xb5, 0x41, 0x87, 0x62, 0xd2, 0xb7, 0x09, 0x12, 0x71, 0x01, 0x2c, 0xac, 0x6d, 0x9d, 0x37, 0x46, 0x5b, 0xdc, 0x76, 0x2c, 0x96, 0x61, 0x88, 0x55, 0x5a, 0x20, 0xc2, 0x84, 0x95, 0xbd, 0x72, 0xc4, 0xb7, 0x22, 0xae, 0xeb, 0x49, 0x0e, 0x16, 0xf1, 0xf1, 0xbf, 0xc5, 0xc7, 0xa8, 0x8d, 0xfb, 0xe1, 0x08, 0x6c, 0xc4, 0x79, 0x81, 0x13, 0xe8, 0x39, 0xbf, 0x6e, 0x5c, 0xa1, 0x62, 0xfb, 0x32, 0x2a, 0x62, 0xf0, 0x12, 0x07, 0x31, 0x93, 0x40, 0xf3, 0xc0, 0xea, 0x1d, 0xd8, 0x65, 0xba, 0x12, 0xb3, 0x9b, 0xf5, 0x59, 0x9c, 0x4e, 0xf6, 0xb9, 0xf7, 0x85, 0xa1, 0xd9, 0x2f, 0x7c, 0x8b, 0xd0, 0xfc, 0x53, 0x3b, 0xed, 0x85, 0xa4, 0xd2, 0x5e, 0x69, 0x61, 0x02, 0x53, 0xb6, 0x19, 0xc7, 0x82, 0xea, 0x8a, 0x45, 0x01, 0x5d, 0x4b, 0xb3, 0x06, 0x86, 0x7f, 0x4b, 0x2f, 0xe7, 0xa8, 0xd0, 0x28, 0x62, 0x02, 0xe8, 0xf3, 0x9e, 0x1e, 0x72, 0x82, 0x07, 0x9f, 0xdd, 0xd2, 0x83, 0x7d, 0x89, 0x73, 0x1b, 0x6f, 0x35, 0x20, 0xb7, 0x88, 0x15, 0x92, 0xa7, 0x11, 0xfe, 0x81, 0x68, 0xed, 0x14, 0x07, 0xdf, 0x4a, 0x06, 0x9c, 0x5e, 0x7e, 0x34, 0x3a, 0x2a, 0x8a, 0xd3, 0xe8, 0xf8, 0xd4, 0xdb, 0xe3, 0xe9, 0x73, 0xbf, 0xa7, 0xe9, 0x73, 0x62, 0xf2, 0x9d, 0xc1, 0xf7, 0x51, 0xeb, 0xff, 0xb7, 0xe6, 0xd9, 0xac, 0x46, 0x06, 0x74, 0xe2, 0x25, 0x3f, 0x46, 0x43, 0xce, 0x49, 0x52, 0x25, 0x1b, 0xf9, 0x24, 0x5c, 0xda, 0xfd, 0x7f, 0xf6, 0xef, 0xb3, 0xd5, 0xe9, 0x6e, 0x35, 0xb8, 0xd1, 0x0e, 0x2c, 0xc1, 0x48, 0x5a, 0x27, 0x0a, 0x81, 0x01, 0x0f, 0xe4, 0x51, 0xcf, 0x89, 0x36, 0xd3, 0xe8, 0x5e, 0x05, 0xb9, 0x83, 0x42, 0xf3, 0xa5, 0x94, 0x67, 0x6d, 0x6a, 0x6e, 0xad, 0xf8, 0x90, 0xb1, 0x1d, 0x63, 0x18, 0x52, 0xc1, 0xbf, 0xbc, 0xad, 0xf4, 0xd2, 0xc5, 0xef, 0xca, 0x4c, 0xfe, 0xa1, 0xda, 0x15, 0x92, 0x4c, 0x42, 0x3d, 0xfc, 0x80, 0x7e, 0x49, 0x13, 0x4e, 0xf6, 0xe1, 0xee, 0x70, 0xca, 0xd9, 0x0a, 0xde, 0x9b, 0xea, 0xcd, 0xf9, 0x90, 0xfd, 0xae, 0x09, 0xce, 0xb6, 0xa0, 0xf7, 0xd1, 0xe6, 0x0c, 0x55, 0x1e, 0x3f, 0xbb, 0x1e, 0xff, 0x3d, 0xdb, 0xdd, 0x27, 0x80, 0x06, 0x53, 0x7e, 0x0b, 0x2a, 0x80, 0x24, 0x51, 0x5c, 0x6a, 0xab, 0x32, 0x5d, 0x37, 0x8a, 0xf4, 0xb7, 0x11, 0xa7, 0xc1, 0x9e, 0x05, 0x2c, 0x16, 0xc2, 0x08, 0xe2, 0xac, 0x1a, 0xeb, 0x60, 0xf8, 0xd2, 0xea, 0x39, 0x01, 0x1c, 0x64, 0xbd, 0x22, 0x80, 0x19, 0x20, 0xc9, 0x6f, 0xdd, 0x5c, 0x73, 0x8c, 0xa1, 0x53, 0x48, 0x2e, 0x99, 0x1d, 0xc0, 0x8f, 0x28, 0xf1, 0xe3, 0xc5, 0xc5, 0x65, 0x53, 0xf2, 0x44, 0x44, 0x24, 0xb9, 0xe2, 0x73, 0xe4, 0x76, 0x14, 0x56, 0xb8, 0x82, 0xe3, 0xb4, 0xfd, 0x68, 0x31, 0xed, 0x40, 0x10, 0x99, 0xd3, 0x3d, 0xe5, 0x6b, 0x14, 0x61, 0x66, 0x9a, 0xf6, 0x33, 0x98, 0xc5, 0x4d, 0x11, 0xbb, 0xf8, 0x56, 0xf8, 0x8f, 0xd7, 0xb9, 0xda, 0xa3, 0x56, 0x1a, 0xe0, 0x9e, 0xbe, 0x5f, 0x56, 0xe5, 0xb9, 0xd8, 0xf3, 0xbc, 0x19, 0xf5, 0xe9, 0x1f, 0xd2, 0xea, 0xf4, 0x5a, 0xde, 0xed, 0xd4, 0x9e, 0xc8, 0xf5, 0x54,
        0x83, 0x8b, 0x8c, 0x2d, 0x24, 0x0e, 0x30, 0xb1, 0x84, 0xa2, 0xbe, 0x2c, 0x86, 0xe6, 0x42, 0x82, 0xaa, 0x37, 0x64, 0x55, 0x51, 0xbc, 0xde, 0xc0, 0x63, 0x88, 0xf6, 0x31, 0x71, 0x52, 0xd5, 0x34, 0x0f, 0x8e, 0xcb, 0x28, 0x65, 0x93, 0x1a, 0x66, 0x3b, 0x21, 0x00, 0xaa, 0x7a, 0xda, 0x2d, 0xf6, 0x7e, 0xb5, 0x27, 0x79, 0xf4, 0x50, 0x3b, 0x10, 0x6b, 0x3c, 0xd7, 0x99, 0x9d, 0xf6, 0xc5, 0x01, 0x91, 0xa0, 0xd5, 0x4f, 0xd3, 0x76, 0x54, 0xa8, 0x5c, 0x35, 0x1d, 0xe2, 0x35, 0x6a, 0x68, 0x67, 0x03, 0xc4, 0x1f, 0xe9, 0x60, 0xb8, 0x49, 0xb1, 0x9a, 0x40, 0xd9, 0x3c, 0x4c, 0x73, 0xaa, 0x88, 0x63, 0xaf, 0xfe, 0xe8, 0xa8, 0x0c, 0x96, 0xbe, 0xb4, 0x65, 0x7c, 0x27, 0xfb, 0xc1, 0x27, 0x24, 0x58, 0xab, 0x4b, 0xa0, 0x5a, 0x7d, 0xc7, 0xca, 0x2d, 0xa5, 0x22, 0xa7, 0xed, 0x26, 0x87, 0xd5, 0x44, 0x1a, 0xc7, 0xdd, 0xfb, 0x60, 0xfc, 0xe5, 0x50, 0xd9, 0x8d, 0xa7, 0xdb, 0x78, 0xb6, 0x9d, 0x80, 0x0f, 0xb9, 0x5f, 0xa7, 0x53, 0x92, 0x5d, 0x18, 0xce, 0x89, 0xc2, 0x69, 0xee, 0xcf, 0xb6, 0x66, 0xe5, 0x66, 0xd2, 0xe3, 0x35, 0x74, 0x0b, 0x83, 0xb6, 0xde, 0xf1, 0xfb, 0xb4, 0x1d, 0x4b, 0x94, 0x95, 0x06, 0x82, 0xe7, 0x1c, 0xf8, 0xc5, 0xe6, 0xd0, 0xf2, 0x17, 0x37, 0x44, 0xfe, 0x99, 0x43, 0x82, 0xbb, 0x88, 0xe4, 0x43, 0x67, 0xcc, 0x4d, 0x5f, 0xa6, 0x26, 0xd7, 0x53, 0xd6, 0x45, 0x96, 0x2b, 0x63, 0xd1, 0x2a, 0xa1, 0x2c, 0x41, 0x59, 0x8b, 0xb8, 0xc1, 0x89, 0x03, 0x3a, 0x61, 0x13, 0xc4, 0x2c, 0x37, 0xa5, 0xbf, 0xd7, 0xdb, 0xd8, 0x53, 0x5f, 0xa1, 0xdb, 0xdb, 0xa5, 0x73, 0xb6, 0xf7, 0x74, 0xa0, 0xf8, 0x93, 0xf5, 0x61, 0xee, 0x3c, 0xe7, 0x00, 0x01, 0x98, 0xe0, 0xa1, 0x22, 0xb6, 0x9a, 0x83, 0x44, 0xa1, 0xe6, 0x70, 0x56, 0x65, 0x92, 0x1e, 0xf0, 0xbc, 0x73, 0xa5, 0x7a, 0xc1, 0x1a, 0x02, 0xf9, 0xd4, 0xc4, 0x7c, 0x81, 0xda, 0x15, 0xc0, 0xd4, 0x25, 0xdc, 0x17, 0xa6, 0x0d, 0x90, 0x55, 0xf2, 0x10, 0xf8, 0xa7, 0x71, 0x9b, 0xed, 0xdf, 0xdf, 0xa1, 0xe4, 0xb9, 0x12, 0x6b, 0x05, 0x3e, 0x83, 0x99, 0x49, 0xbf, 0x66, 0xbb, 0xf6, 0x76, 0xd3, 0xa9, 0x24, 0x61, 0x8c, 0x25, 0x49, 0xd0, 0xf7, 0x83, 0x44, 0xfb, 0x27, 0xe2, 0x7d, 0x69, 0x6d, 0x34, 0x67, 0xed, 0x39, 0x89, 0x02, 0xcb, 0x2f, 0x33, 0x3c, 0xcd, 0x12, 0x42, 0x8f, 0x86, 0x7d, 0xda, 0x3f, 0xd7, 0x26, 0x62, 0x9c, 0x1f, 0x2e, 0xa8, 0xc3, 0x85, 0xf1, 0x73, 0xe5, 0x2c, 0x11, 0xde, 0x98, 0xc8, 0xb0, 0x10, 0x17, 0x55, 0xf5, 0x32, 0x52, 0x67, 0xca, 0x64, 0x50, 0x28, 0x9a, 0x24, 0x92, 0xa1, 0x97, 0x57, 0x81, 0xaf, 0xca, 0x1e, 0xc0, 0xa4, 0x71, 0x2d, 0x2a, 0xec, 0xc9, 0x23, 0x6a, 0x0c, 0x1d, 0x54, 0x15, 0x2a, 0x56, 0x42, 0x0a, 0x83, 0xff, 0x28, 0xba, 0xe7, 0x68, 0x38, 0xf5, 0x32, 0xa9, 0xb7, 0xe7, 0x70, 0x32, 0xa8, 0x79, 0x5e, 0x46, 0x1d, 0xec, 0x29, 0x8a, 0xde, 0x41, 0x94, 0x94, 0x26, 0x79, 0xc2, 0x52, 0x23, 0xe0, 0xa1, 0x1d, 0x65, 0x0c, 0xbe, 0x1b, 0x87, 0x2a, 0x21, 0x53, 0x2f, 0x35, 0x56, 0xe8, 0xd1, 0x7b, 0xb8, 0x23, 0x75, 0x56, 0xc7, 0x08, 0x9d, 0x13, 0xf0, 0x8f, 0x80, 0x38, 0xe9, 0x92, 0xf7, 0x16, 0xc2, 0xf3, 0x74, 0xa7, 0x92, 0xf5, 0x49, 0x7d, 0x09, 0x41, 0xbc, 0x07, 0x61, 0x1f, 0xe6, 0xa0, 0xd8, 0xa6, 0xe3, 0x72, 0xa4, 0x59, 0x4a, 0xd9, 0x33, 0x40, 0x80, 0x3a, 0x3a, 0xb3, 0xa0, 0x96, 0xca, 0x56, 0x98, 0xbd, 0x1f, 0x80, 0x86, 0x6c, 0xe1, 0x09, 0x64, 0x1b, 0x1a, 0xc9, 0x52, 0xaa, 0xd1, 0x39, 0xea, 0x4b, 0x6a, 0x3e, 0x4e, 0xa4, 0xea, 0x00, 0xde, 0x07, 0x0b, 0x23, 0xbc, 0x40, 0xc4, 0xd2, 0xd9, 0xf6, 0xda, 0x8e, 0x22, 0x36, 0xbe, 0x5e, 0x65, 0x6e, 0xbe, 0xc8, 0xb0, 0x07, 0xa2, 0x2d, 0xe9, 0x4b, 0x73, 0x54, 0xe6, 0x0a, 0xf2, 0xd3, 0x83, 0x8b, 0x27, 0x4c, 0xcc, 0x0c, 0x8a, 0xd4, 0x2b, 0xb8, 0x95, 0x2e, 0x42, 0x64, 0x29, 0xc1, 0xe0, 0x6b, 0x92, 0xab, 0xfe, 0x53, 0x06, 0x96, 0x4a, 0x8c, 0x5d, 0x7c, 0x51, 0x74, 0xd0, 0x1e, 0x37, 0x35, 0x9c, 0x1e, 0x69, 0x8f, 0x68, 0x18, 0xd9, 0xbe, 0xaf, 0x81, 0x9b, 0x7e, 0xd8, 0x71, 0x9d, 0xb6, 0x50, 0x43, 0x78, 0x85, 0x7d, 0x65, 0x93, 0x45, 0xb4, 0x02, 0xd0, 0x5c, 0x36, 0xe2, 0x62, 0x3f, 0x40, 0x33, 0xee, 0x91, 0xe5, 0x3f, 0x67, 0x39, 0x2f, 0x1b, 0x89, 0x9f, 0x04, 0x9d, 0x46, 0x3e, 0x70, 0x92, 0x9e, 0x8c, 0xf5
    };
    uint8_t s0s1s2[] = {
        0x03, 0xac, 0x44, 0x29, 0x53, 0x04, 0x05, 0x00, 0x01, 0x6e, 0x65, 0x69, 0x2d, 0x69, 0x2d, 0x69, 0x73, 0x6e, 0x69, 0x73, 0x6c, 0x65, 0x72, 0x69, 0x72, 0x76, 0x65, 0x72, 0x69, 0x77, 0x74, 0x2e, 0x6e, 0x72, 0x76, 0x72, 0x65, 0x72, 0x70, 0x72, 0x69, 0x69, 0x70, 0x72, 0x73, 0x6e, 0x65, 0x72, 0x72, 0x6e, 0x2d, 0x65, 0x74, 0x72, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x40, 0x69, 0x69, 0x76, 0x77, 0x2d, 0x73, 0x65, 0x72, 0x72, 0x76, 0x73, 0x72, 0x2e, 0x2d, 0x76, 0x65, 0x31, 0x65, 0x6d, 0x6d, 0x73, 0x69, 0x73, 0x74, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x72, 0x65, 0x2d, 0x74, 0x69, 0x31, 0x65, 0x2d, 0x6f, 0x77, 0x2e, 0x76, 0x77, 0x2d, 0x77, 0x72, 0x65, 0x65, 0x31, 0x74, 0x73, 0x70, 0x74, 0x6e, 0x72, 0x6e, 0x73, 0x6d, 0x2e, 0x69, 0x72, 0x2d, 0x65, 0x69, 0x77, 0x69, 0x76, 0x72, 0x77, 0x72, 0x32, 0x6e, 0x65, 0x6c, 0x2e, 0x2d, 0x6e, 0x69, 0x6d, 0x6c, 0x73, 0x65, 0x73, 0x70, 0x2d, 0x65, 0x72, 0x40, 0x72, 0x74, 0x6e, 0x6e, 0x6d, 0x6f, 0x70, 0x74, 0x73, 0x2d, 0x63, 0x69, 0x32, 0x31, 0x2d, 0x40, 0x69, 0x70, 0x2d, 0x2d, 0x72, 0x69, 0x74, 0x63, 0x6f, 0x69, 0x69, 0x65, 0x6e, 0x32, 0x6f, 0x6c, 0x6e, 0x72, 0x73, 0x77, 0x65, 0x65, 0x72, 0x32, 0x6d, 0x65, 0x6c, 0x2d, 0x72, 0x6e, 0x65, 0x6d, 0x31, 0x65, 0x74, 0x2d, 0x6f, 0x72, 0x65, 0x63, 0x69, 0x40, 0x70, 0x2d, 0x65, 0x6d, 0x2d, 0x77, 0x63, 0x63, 0x74, 0x40, 0x36, 0x2d, 0x72, 0x65, 0x70, 0x2d, 0x6e, 0x69, 0x6d, 0x65, 0x74, 0x70, 0x76, 0x40, 0x76, 0x72, 0x72, 0x69, 0x77, 0x76, 0x69, 0x74, 0x74, 0x65, 0x31, 0x6d, 0x2e, 0x6f, 0x72, 0x73, 0x73, 0x6c, 0x40, 0x36, 0x72, 0x70, 0x72, 0x70, 0x72, 0x69, 0x32, 0x6c, 0x77, 0x70, 0x76, 0x65, 0x72, 0x76, 0x63, 0x65, 0x65, 0x77, 0x72, 0x6e, 0x2e, 0x76, 0x69, 0x69, 0x2e, 0x40, 0x72, 0x2e, 0x2e, 0x72, 0x73, 0x6e, 0x72, 0x72, 0x6e, 0x70, 0x40, 0x77, 0x65, 0x77, 0x65, 0x70, 0x63, 0x74, 0x2d, 0x70, 0x72, 0x2d, 0x74, 0x72, 0x31, 0x65, 0x6e, 0x2d, 0x76, 0x2d, 0x2d, 0x2d, 0x74, 0x76, 0x2d, 0x74, 0x65, 0x2e, 0x2d, 0x6c, 0x76, 0x2d, 0x6c, 0x70, 0x73, 0x6d, 0x65, 0x72, 0x31, 0x31, 0x36, 0x76, 0x73, 0x73, 0x6e, 0x2d, 0x6e, 0x73, 0x72, 0x2d, 0x6f, 0x6c, 0x65, 0x74, 0x77, 0x65, 0x69, 0x72, 0x69, 0x65, 0x6d, 0x76, 0x31, 0x65, 0x73, 0x72, 0x6c, 0x72, 0x77, 0x65, 0x76, 0x74, 0x72, 0x69, 0x72, 0x76, 0x32, 0x73, 0x6d, 0x72, 0x2d, 0x6d, 0x40, 0x69, 0x40, 0x69, 0x31, 0x69, 0x6f, 0x6e, 0x6d, 0x69, 0x73, 0x70, 0x72, 0x77, 0x6f, 0x6f, 0x65, 0x77, 0x76, 0x70, 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x32, 0x36, 0x6c, 0x74, 0x6e, 0x72, 0x74, 0x2d, 0x6e, 0x6c, 0x72, 0x72, 0x2d, 0x74, 0x65, 0x73, 0x70, 0x65, 0x72, 0x6c, 0x65, 0x65, 0x2d, 0x6e, 0x70, 0x6e, 0x40, 0x65, 0x6e, 0x6e, 0x74, 0x65, 0x6e, 0x72, 0x6e, 0xfe, 0x5a, 0x38, 0x79, 0x81, 0xe8, 0x49, 0xee, 0x93, 0xbb, 0xa0, 0x59, 0x4a, 0xa0, 0xcc, 0x31, 0xbf, 0x0d, 0x86, 0xc0, 0x3f, 0xae, 0x2a, 0x16, 0xfa, 0xf0, 0x4e, 0x0f, 0xa3, 0x01, 0x06, 0xa0, 0x0e, 0xa5, 0x8c, 0xa4, 0xca, 0xd2, 0x01, 0xa5, 0x90, 0xbd, 0x55, 0xd1, 0x42, 0x2b, 0xd4, 0xb3, 0xbb, 0x06, 0xb1, 0x3a, 0x94, 0x41, 0x76, 0x1d, 0xa5, 0x23, 0x6e, 0x1e, 0x59, 0x73, 0x63, 0x34, 0x60, 0xd3, 0x48, 0xc0, 0x3b, 0xcf, 0xf1, 0xa8, 0x38, 0xd6, 0xf3, 0x5e, 0x6d, 0xcb, 0xea, 0xfc, 0x9c, 0x52, 0xae, 0x9a, 0x89, 0xdb, 0x24, 0x1b, 0x92, 0x4a, 0x85, 0x97, 0x3c, 0xd8, 0x4c, 0x31, 0xad, 0xfd, 0x00, 0xef, 0xc5, 0x17, 0xa5, 0x22, 0xc0, 0xf1, 0x94, 0x18, 0xec, 0xf6, 0x49, 0xe5, 0x05, 0x11, 0x12, 0x67, 0x6c, 0x71, 0xc0, 0x84, 0x6d, 0x50, 0xf8, 0x23, 0x01, 0x57, 0xc4, 0xfc, 0x73, 0x65, 0x69, 0x6e, 0x65, 0x72, 0x6d, 0x6f, 0x69, 0x2d, 0x65, 0x65, 0x69, 0x63, 0x63, 0x69, 0x2d, 0x72, 0x2d, 0x69, 0x2d, 0x2d, 0x77, 0x72, 0x76, 0x72, 0x72, 0x2d, 0x76, 0x70, 0x63, 0x69, 0x74, 0x73, 0x6d, 0x65, 0x6c, 0x2d, 0x73, 0x6c, 0x65, 0x6e, 0x73, 0x77, 0x69, 0x63, 0x69, 0x70, 0x31, 0x40, 0x72, 0x69, 0x2d, 0x2d, 0x2d, 0x72, 0x72, 0x6c, 0x72, 0x63, 0x72, 0x77, 0x6e, 0x6c, 0x2d, 0x72, 0x2e, 0x76, 0x72, 0x65, 0x6d, 0x76, 0x36, 0x6d, 0x72, 0x77, 0x72, 0x65, 0x65, 0x69, 0x72, 0x76, 0x6d, 0x76, 0x74, 0x76, 0x72, 0x65, 0x69, 0x72, 0x6e, 0x6d, 0x77, 0x6c, 0x40, 0x32, 0x70, 0x65, 0x65, 0x69, 0x72, 0x31, 0x2e, 0x70, 0x36, 0x31, 0x65, 0x70, 0x72, 0x72, 0x73, 0x72, 0x6e, 0x6e, 0x73, 0x32, 0x2d, 0x2d, 0x2d, 0x69, 0x65, 0x31, 0x74, 0x6e, 0x65, 0x74, 0x65, 0x76, 0x69, 0x6d, 0x6c, 0x6e, 0x70, 0x74, 0x73, 0x72, 0x6d, 0x72, 0x72, 0x69, 0x65, 0x74, 0x65, 0x65, 0x2d, 0x70, 0x74, 0x6e, 0x74, 0x65, 0x6f, 0x72, 0x69, 0x76, 0x40, 0x31, 0x69, 0x72, 0x6d, 0x6d, 0x77, 0x69, 0x72, 0x65, 0x6e, 0x40, 0x63, 0x40, 0x65, 0x65, 0x69, 0x2d, 0x72, 0x65, 0x40, 0x69, 0x32, 0x74, 0x73, 0x6e, 0x36, 0x2d, 0x70, 0x65, 0x6c, 0x70, 0x6e, 0x72, 0x69, 0x32, 0x65, 0x74, 0x76, 0x77, 0x73, 0x6f, 0x77, 0x65, 0x72, 0x2d, 0x6e, 0x73, 0x65, 0x65, 0x70, 0x65, 0x2d, 0x65, 0x73, 0x2d, 0x65, 0x2e, 0x73, 0x69, 0x67, 0x45, 0x8b, 0x6b, 0x3b, 0xc9, 0x5f, 0x09, 0x65, 0x65, 0x72, 0x6c, 0x73, 0x6d, 0x70, 0x70, 0x73, 0x63, 0x70, 0x40, 0x72, 0x76, 0x65, 0x6e, 0x6f, 0x6c, 0x69, 0x2e, 0x72, 0x73, 0x76, 0x69, 0x77, 0x72, 0x2d, 0x69, 0x6e, 0x69, 0x65, 0x77, 0x73, 0x69, 0x70, 0x77, 0x63, 0x65, 0x74, 0x72, 0x73, 0x31, 0x65, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x6d, 0x65, 0x36, 0x6e, 0x6e, 0x69, 0x6d, 0x6e, 0x70, 0x77, 0x72, 0x65, 0x31, 0x65, 0x6e, 0x6e, 0x65, 0x2d, 0x65, 0x65, 0x2e, 0x77, 0x6e, 0x6f, 0x2d, 0x76, 0x65, 0x72, 0x6c, 0x31, 0x76, 0x65, 0x72, 0x2d, 0x36, 0x6c, 0x70, 0x6f, 0x65, 0x72, 0x73, 0x63, 0x72, 0x77, 0x73, 0x72, 0x65, 0x65, 0x65, 0x6c, 0x76, 0x72, 0x65, 0x6e, 0x65, 0x2e, 0x6f, 0x2d, 0x72, 0x70, 0x65, 0x74, 0x72,
        0x77, 0x69, 0x69, 0x72, 0x65, 0x77, 0x6c, 0x72, 0x2d, 0x69, 0x72, 0x31, 0x6e, 0x65, 0x70, 0x72, 0x74, 0x76, 0x6c, 0x2e, 0x72, 0x65, 0x72, 0x6c, 0x73, 0x6c, 0x2e, 0x2e, 0x72, 0x2d, 0x6e, 0x63, 0x32, 0x2e, 0x65, 0x2d, 0x65, 0x69, 0x2d, 0x65, 0x70, 0x6e, 0x72, 0x72, 0x32, 0x2e, 0x73, 0x70, 0x77, 0x65, 0x73, 0x77, 0x73, 0x40, 0x40, 0x73, 0x63, 0x2e, 0x65, 0x76, 0x70, 0x65, 0x69, 0x65, 0x70, 0x73, 0x40, 0x65, 0x73, 0x2d, 0x2d, 0x2e, 0x2e, 0x73, 0x65, 0x6f, 0x65, 0x65, 0x6d, 0x76, 0x70, 0x6d, 0x69, 0x70, 0x70, 0x69, 0x2e, 0x76, 0x6e, 0x72, 0x72, 0x72, 0x6d, 0x73, 0x6f, 0x73, 0x72, 0x72, 0x72, 0x77, 0x70, 0x65, 0x69, 0x72, 0x73, 0x6e, 0x69, 0x65, 0x65, 0x74, 0x65, 0x69, 0x40, 0x63, 0x69, 0x70, 0x6c, 0x6e, 0x2d, 0x65, 0x69, 0x72, 0x63, 0x6c, 0x72, 0x2e, 0x36, 0x69, 0x72, 0x6c, 0x6c, 0x2d, 0x6f, 0x76, 0x69, 0x6f, 0x2d, 0x6d, 0x6c, 0x72, 0x72, 0x2e, 0x70, 0x73, 0x6d, 0x6f, 0x2e, 0x6e, 0x69, 0x65, 0x65, 0x2d, 0x6d, 0x76, 0x6e, 0x69, 0x73, 0x73, 0x73, 0x74, 0x63, 0x65, 0x76, 0x2e, 0x77, 0x2d, 0x36, 0x73, 0x69, 0x2d, 0x72, 0x72, 0x6c, 0x36, 0x74, 0x72, 0x6d, 0x65, 0x2d, 0x65, 0x2e, 0x6d, 0x31, 0x72, 0x6f, 0x74, 0x76, 0x31, 0x65, 0x6d, 0x69, 0x72, 0x69, 0x69, 0x2d, 0x72, 0x73, 0x72, 0x72, 0x76, 0x31, 0x6e, 0x2d, 0x69, 0x6e, 0x77, 0x70, 0x69, 0x72, 0x6e, 0x76, 0x74, 0x6f, 0x65, 0x63, 0x6f, 0x73, 0x65, 0x73, 0x72, 0x69, 0x69, 0x40, 0x6e, 0x65, 0x65, 0x65, 0x65, 0x77, 0x70, 0x70, 0x6e, 0x72, 0x6e, 0x65, 0x72, 0x32, 0x65, 0x2d, 0x77, 0x69, 0x6e, 0x70, 0x69, 0x6f, 0x76, 0x77, 0x72, 0x74, 0x77, 0x6e, 0x72, 0xfe, 0x98, 0xf3, 0xb4, 0xff, 0x3f, 0x2e, 0xdb, 0x59, 0xbd, 0x32, 0x02, 0x6a, 0x44, 0x03, 0x67, 0x9e, 0xe1, 0x98, 0x97, 0xed, 0x67, 0x6d, 0xb0, 0x8f, 0xa9, 0xb6, 0xf8, 0x4d, 0x92, 0x35, 0x19, 0x72, 0x72, 0x65, 0x74, 0x73, 0x6e, 0x65, 0x65, 0x69, 0x36, 0x72, 0x73, 0x2d, 0x70, 0x2d, 0x2d, 0x69, 0x6e, 0x72, 0x65, 0x32, 0x72, 0x77, 0x72, 0x73, 0x77, 0x73, 0x70, 0x2d, 0x2d, 0x69, 0x6c, 0x70, 0x74, 0x65, 0x69, 0x72, 0x74, 0x6e, 0x76, 0x65, 0x76, 0x76, 0x69, 0x69, 0x65, 0x70, 0x6e, 0x73, 0x6e, 0x36, 0x76, 0x70, 0x76, 0x6c, 0x6c, 0x70, 0x6e, 0x6e, 0x74, 0x2e, 0x6f, 0x32, 0x74, 0x76, 0x74, 0x40, 0x72, 0x6e, 0x72, 0x74, 0x74, 0x2d, 0x6f, 0x72, 0x73, 0x32, 0x72, 0x32, 0x72, 0x70, 0x65, 0x65, 0x6e, 0x72, 0x70, 0x73, 0x72, 0x69, 0x74, 0x74, 0x6e, 0x72, 0x6c, 0x31, 0x74, 0x77, 0x31, 0x63, 0x63, 0x74, 0x69, 0x72, 0x69, 0x72, 0x70, 0x31, 0x74, 0x72, 0x76, 0x65, 0x72, 0x65, 0x6c, 0x76, 0x6d, 0x72, 0x6c, 0x69, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x69, 0x6c, 0x74, 0x6e, 0x65, 0x69, 0x77, 0x73, 0x70, 0x69, 0x72, 0x2d, 0x65, 0x74, 0x2e, 0x65, 0x65, 0x6d, 0x72, 0x31, 0x2d, 0x72, 0x36, 0x65, 0x2d, 0x69, 0x6d, 0x36, 0x6e, 0x72, 0x6d, 0x6c, 0x72, 0x72, 0x65, 0x65, 0x6e, 0x31, 0x6e, 0x40, 0x72, 0x40, 0x6f, 0x73, 0x6d, 0x36, 0x2e, 0x72, 0x65, 0x36, 0x74, 0x77, 0x65, 0x65, 0x73, 0x36, 0x76, 0x6c, 0x6f, 0x2d, 0x36, 0x6d, 0x36, 0x70, 0x32, 0x74, 0x6d, 0x65, 0x6d, 0x69, 0x65, 0x65, 0x69, 0x76, 0x69, 0x74, 0x2d, 0x63, 0x2d, 0x6e, 0x32, 0x72, 0x63, 0x2d, 0x77, 0x72, 0x74, 0x72, 0x70, 0x6e, 0x76, 0x6f, 0x72, 0x40, 0x65, 0x65, 0x6d, 0x77, 0x2d, 0x2d, 0x74, 0x6e, 0x73, 0x76, 0x65, 0x69, 0x69, 0x72, 0x6f, 0x65, 0x70, 0x69, 0x6d, 0x76, 0x69, 0x65, 0x72, 0x2d, 0x74, 0x2d, 0x69, 0x65, 0x72, 0x69, 0x6f, 0x72, 0x72, 0x69, 0x76, 0x72, 0x77, 0x69, 0x2e, 0x77, 0x69, 0x70, 0x69, 0x6d, 0x36, 0x72, 0x76, 0x65, 0x76, 0x73, 0x6e, 0x72, 0x65, 0x2e, 0x76, 0x2d, 0x76, 0x6f, 0x2d, 0x65, 0x73, 0x72, 0x74, 0x36, 0x2d, 0x6f, 0x70, 0x73, 0x74, 0x74, 0x77, 0x6c, 0x2d, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x72, 0x32, 0x2d, 0x72, 0x69, 0x6d, 0x6e, 0x72, 0x6c, 0x6f, 0x65, 0x36, 0x31, 0x65, 0x65, 0x69, 0x73, 0x31, 0x74, 0x69, 0x69, 0x65, 0x40, 0x69, 0x6e, 0x2d, 0x63, 0x40, 0x31, 0x70, 0x65, 0x6e, 0x2d, 0x69, 0x72, 0x65, 0x65, 0x76, 0x65, 0x70, 0x72, 0x6c, 0x2d, 0x6e, 0x73, 0x69, 0x65, 0x65, 0x6e, 0x2e, 0x63, 0x6c, 0x72, 0x65, 0x2d, 0x2e, 0x6d, 0x72, 0x76, 0x70, 0x69, 0x6d, 0x40, 0x32, 0x77, 0x72, 0x6e, 0x72, 0x6c, 0x36, 0x72, 0x31, 0x2d, 0x73, 0x74, 0x2d, 0x69, 0x63, 0x40, 0x70, 0x32, 0x65, 0x31, 0x69, 0x69, 0x65, 0x72, 0x63, 0x74, 0x72, 0x74, 0x77, 0x6e, 0x69, 0x72, 0x65, 0x76, 0x65, 0x77, 0x69, 0x69, 0x73, 0x6e, 0x77, 0x77, 0x73, 0x6f, 0x69, 0x70, 0x73, 0x2d, 0x65, 0x65, 0x73, 0x65, 0x77, 0x2d, 0x73, 0x72, 0x6d, 0x65, 0x32, 0x6e, 0x73, 0x36, 0x65, 0x72, 0x77, 0x70, 0x65, 0x69, 0x2d, 0x2d, 0x74, 0x6f, 0x6f, 0x36, 0x63, 0x74, 0x72, 0x63, 0x77, 0x69, 0x2e, 0x31, 0x6c, 0x65, 0x77, 0x72, 0x65, 0x76, 0x74, 0x2d, 0x77, 0x2e, 0x76, 0x72, 0x6e, 0x36, 0x70, 0x69, 0x2e, 0x6e, 0x72, 0x77, 0x69, 0x65, 0x74, 0x2d, 0x6e, 0x63, 0x6e, 0x70, 0x2d, 0x6e, 0x2e, 0x6d, 0x69, 0x65, 0x63, 0x65, 0x2d, 0x76, 0x70, 0x73, 0x31, 0x73, 0x2d, 0x76, 0x6e, 0x6e, 0x6c, 0x2d, 0x6c, 0x2d, 0x65, 0x6e, 0x73, 0x6c, 0x65, 0x74, 0x70, 0x65, 0x2d, 0x6e, 0x77, 0x76, 0x40, 0x69, 0x40, 0x63, 0x6f, 0x72, 0x32, 0x6d, 0x31, 0x72, 0x36, 0x69, 0x73, 0x72, 0x70, 0x65, 0x2d, 0x6c, 0x2e, 0x72, 0x74, 0x74, 0x65, 0x69, 0x6f, 0x69, 0x2d, 0x2d, 0x65, 0x6f, 0x65, 0x74, 0x72, 0x69, 0x76, 0x72, 0x72, 0x65, 0x69, 0x76, 0x69, 0x69, 0x6e, 0x31, 0x65, 0x76, 0x72, 0x73, 0x77, 0x72, 0x2d, 0x69, 0x65, 0x69, 0x70, 0x65, 0x6e, 0x6e, 0x65, 0x65, 0x6e, 0x2d, 0x72, 0x76, 0x72, 0x6c, 0x2e, 0x70, 0x76, 0x6e, 0x69, 0x72, 0x70, 0x73, 0x2d, 0x69, 0x74, 0x76, 0x72, 0x70, 0x65, 0x63, 0x72, 0x70, 0x6e, 0x36, 0x6c, 0x74, 0x72, 0x72, 0x72, 0x73, 0x65, 0x40, 0x63, 0x6d, 0x63, 0x32, 0x65, 0x32, 0x69, 0x6e,
        0x77, 0x65, 0x74, 0x72, 0x77, 0x40, 0x69, 0x65, 0x70, 0x31, 0x36, 0x72, 0x73, 0x2d, 0x72, 0x72, 0x32, 0x72, 0x6c, 0x77, 0x6e, 0x6f, 0x77, 0x6c, 0x74, 0x72, 0x2d, 0x6e, 0x65, 0x70, 0x6c, 0x72, 0x6f, 0x69, 0x2d, 0x2d, 0x69, 0x36, 0x69, 0x69, 0x76, 0x69, 0x69, 0x6d, 0x72, 0x73, 0x6f, 0x6d, 0x74, 0x70, 0x76, 0x6d, 0x6d, 0x69, 0x72, 0x70, 0x70, 0x2d, 0x31, 0x63, 0x6c, 0x65, 0x65, 0x6e, 0x2d, 0x77, 0x74, 0x73, 0x6c, 0x72, 0x6e, 0x65, 0x65, 0x2d, 0x6c, 0x69, 0x2d, 0x6e, 0x74, 0x70, 0x72, 0x77, 0x77, 0x65, 0x65, 0x65, 0x2d, 0x76, 0x6e, 0x72, 0x69, 0x69, 0x73, 0x65, 0x74, 0x73, 0x76, 0x72, 0x72, 0x72, 0x69, 0x72, 0x73, 0x72, 0x6f, 0x2e, 0x77, 0x2d, 0x2d, 0x6c, 0x6e, 0x65, 0x65, 0x6d, 0x73, 0x2e, 0x72, 0x65, 0x73, 0x40, 0x69, 0x32, 0x69, 0x32, 0x6e, 0x65, 0x32, 0x65, 0x74, 0x6d, 0x65, 0x74, 0x36, 0x6e, 0x72, 0x32, 0x6e, 0x65, 0x69, 0x32, 0x6f, 0x70, 0x72, 0x72, 0x65, 0x72, 0x72, 0x69, 0x6e, 0x6d, 0x69, 0x70, 0x6c, 0x6c, 0x65, 0x31, 0x72, 0x72, 0x73, 0x72, 0x70, 0x73, 0x72, 0x65, 0x65, 0x6e, 0x76, 0x69, 0x6d, 0x65, 0x6c, 0x65, 0x31, 0x74, 0x74, 0x72, 0x63, 0x2e, 0x69, 0x65, 0x2d, 0x6d, 0x72, 0x70, 0x6e, 0x6c, 0x65, 0x31, 0x73, 0x73, 0x40, 0x74, 0x72, 0x73, 0x2e, 0x74, 0x6e, 0x77, 0x6c, 0x6f, 0x70, 0x77, 0x76, 0x73, 0x72, 0x69, 0x77, 0x69, 0x6e, 0x69, 0x2d, 0x72, 0x70, 0x70, 0x73, 0x2e, 0x76, 0x73, 0x65, 0x72, 0x72, 0x74, 0x2d, 0x72, 0x65, 0x76, 0x69, 0x77, 0x72, 0x65, 0x2d, 0x72, 0x69, 0x36, 0x77, 0x77, 0x77, 0x40, 0x2d, 0x6d, 0x69, 0x74, 0x72, 0x2d, 0x32, 0x6f, 0x76, 0x72, 0x2d, 0x2d, 0x65, 0x2e, 0x2e, 0x72, 0x6e, 0x32, 0x74, 0x6c, 0x6e, 0x6c, 0x2e, 0x6d, 0x2d, 0x6f, 0x65, 0x72, 0x2d, 0x6e, 0x65, 0x65, 0x69, 0x40, 0x69, 0x77, 0x65, 0x6c, 0x2d, 0x69, 0x69, 0x65, 0x72, 0x72, 0x32, 0x40, 0x73, 0x65, 0x36, 0x76, 0x73, 0x72, 0x69, 0x63, 0x77, 0x72, 0x6c, 0x72, 0x6e, 0x74, 0x2d, 0x65, 0x69, 0x72, 0x70, 0x6d, 0x65, 0x6c, 0x73, 0x65, 0x6c, 0x32, 0x2d, 0x73, 0x70, 0x2d, 0x31, 0x72, 0x74, 0x2e, 0x65, 0x74, 0x72, 0x74, 0x72, 0x70, 0x69, 0x40, 0x36, 0x2d, 0x74, 0x72, 0x6c, 0x2d, 0x6e, 0x72, 0x6e, 0x6d, 0x63, 0x76, 0x74, 0x6d, 0x70, 0x32, 0x70, 0x69, 0x69, 0x2d, 0x73, 0x72, 0x74, 0x65, 0x74, 0x74, 0x70, 0x2d, 0x31, 0x6c, 0x77, 0x65, 0x72, 0x70, 0x73, 0x36, 0x6c, 0x72, 0x72, 0x65, 0x65, 0x76, 0x69, 0x2e, 0x6e, 0x72, 0x72, 0x36, 0x65, 0x69, 0x72, 0x69, 0x40, 0x6c, 0x74, 0x6c, 0x72, 0x2d, 0x70, 0x74, 0x76, 0x74, 0x6f, 0x72, 0x31, 0x73, 0x70, 0x65, 0x74, 0x69, 0x6e, 0x69, 0x6c, 0x70, 0x72, 0x65, 0x70, 0x72, 0x73, 0x69, 0x2d, 0x6d, 0x63, 0x2d, 0x72, 0x2d, 0x36, 0x73, 0x6e, 0x2d, 0x6d, 0x69, 0x76, 0x76, 0x6d, 0x74, 0x72, 0x77, 0x74, 0x2e, 0x6d, 0x65, 0x2d, 0x65, 0x6d, 0x2e, 0x6c, 0x73, 0x6e, 0x6f, 0x76, 0x31, 0x74, 0x65, 0x65, 0x31, 0x69, 0x65, 0x32, 0x2d, 0x74, 0x2d, 0x77, 0x77, 0x77, 0x2e, 0x70, 0x65, 0x6e, 0x2d, 0x69, 0x32, 0x72, 0x73, 0x74, 0x65, 0x65, 0x69, 0x73, 0x77, 0x77, 0x2e, 0x6e, 0x72, 0x65, 0x70, 0x76, 0x40, 0x77, 0x65, 0x2d, 0x70, 0x36, 0x2d, 0x74, 0x65, 0x2d, 0x69, 0x74, 0x76, 0x69, 0x6e, 0x65, 0x2d, 0x65, 0x73, 0x31, 0x36, 0x69, 0x31, 0x74, 0x76, 0x65, 0x77, 0x6c, 0x6e, 0x6c, 0x32, 0x6e, 0x70, 0x73, 0x69, 0x69, 0x65, 0x72, 0x2d, 0x6e, 0x2d, 0x65, 0x65, 0x6c, 0x32, 0x77, 0x72, 0x69, 0x70, 0x76, 0x32, 0x65, 0x6c, 0x36, 0x65, 0x69, 0x31, 0x6e, 0x72, 0x6c, 0x6d, 0x65, 0x65, 0x77, 0x6e, 0x2d, 0x32, 0x77, 0x69, 0x65, 0x6d, 0x74, 0x77, 0x40, 0x65, 0x6e, 0x77, 0x73, 0x65, 0x72, 0x6c, 0x40, 0x65, 0x65, 0x72, 0x72, 0x74, 0x6e, 0x6c, 0x6d, 0x73, 0x69, 0x76, 0x72, 0x31, 0x2d, 0x65, 0x36, 0x72, 0x2d, 0x70, 0x69, 0x6e, 0x63, 0x31, 0x2d, 0x69, 0x6e, 0x65, 0x2d, 0x65, 0x2e, 0x77, 0x2d, 0x72, 0x76, 0x63, 0x69, 0x2d, 0x6d, 0x70, 0x2d, 0x6c, 0x69, 0x63, 0x69, 0x77, 0x6e, 0x69, 0x77, 0x36, 0x72, 0x69, 0x72, 0x2e, 0x74, 0x72, 0x6e, 0x65, 0x6f, 0x73, 0x2d, 0x2e, 0x72, 0x63, 0x76, 0x74, 0x36, 0x65, 0x72, 0x65, 0x6d, 0x32, 0x72, 0x70, 0x40, 0x65, 0x74, 0x6e, 0x32, 0x70, 0x2d, 0x31, 0x40, 0x6c, 0x65, 0x6c, 0x76, 0x69, 0x69, 0x76, 0x76, 0x73, 0x31, 0x6e, 0x65, 0x74, 0x65, 0x6d, 0x69, 0x2d, 0x72, 0x74, 0x74, 0x6c, 0x31, 0x74, 0x6e, 0x6e, 0x65, 0x77, 0x36, 0x69, 0x69, 0x72, 0x6e, 0x2d, 0x2d, 0x2d, 0x72, 0x73, 0x76, 0x72, 0x72, 0x65, 0x72, 0x65, 0x72, 0x2d, 0x6c, 0x76, 0x77, 0x63, 0x77, 0x72, 0x6d, 0x72, 0x2e, 0x65, 0x73, 0x32, 0x72, 0x36, 0x77, 0x72, 0x72, 0x6d, 0x74, 0x2d, 0x72, 0x2e, 0x73, 0x73, 0x65, 0x77, 0x6e, 0x65, 0x69, 0x65, 0x2d, 0x65, 0x77, 0x6f, 0x74, 0x72, 0x32, 0x40, 0x6e, 0x72, 0x69, 0x6e, 0x32, 0x70, 0x73, 0x72, 0x40, 0x2d, 0x65, 0x69, 0x65, 0x77, 0x65, 0x70, 0x40, 0x36, 0x72, 0x6c, 0x6d, 0x73, 0x69, 0x72, 0x72, 0x74, 0x36, 0x6c, 0x76, 0x65, 0x76, 0x2d, 0x74, 0x6c, 0x72, 0x72, 0x74, 0x6e, 0x73, 0x74, 0x69, 0x72, 0x6d, 0x40, 0x2d, 0x6e, 0x70, 0x73, 0x2d, 0x6d, 0x72, 0x72, 0x70, 0x65, 0x65, 0x36, 0x6e, 0x77, 0x2d, 0x69, 0x2d, 0x32, 0x72, 0x6d, 0x72, 0x6c, 0x32, 0x6c, 0x73, 0x6d, 0x65, 0x36, 0x69, 0x69, 0x72, 0x77, 0x74, 0x6f, 0x72, 0x6d, 0x6d, 0x69, 0x65, 0x73, 0x63, 0x65, 0x74, 0x74, 0x72, 0x65, 0x72, 0x2e, 0x6e, 0x73, 0x65, 0x76, 0x6c, 0x76, 0x77, 0x72, 0x6e, 0x6c, 0x32, 0x2d, 0x73, 0x65, 0x73, 0x2e, 0x76, 0x72, 0x65, 0x2d, 0x72, 0x77, 0x2d, 0x77, 0x70, 0x65, 0x6c, 0x72, 0x6e, 0x2e, 0x31, 0x73, 0x2e, 0x72, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x6d, 0x32, 0x70, 0x76, 0x76, 0x31, 0x76, 0x77, 0x65, 0x6e, 0x73, 0x63, 0x2e, 0x2d, 0x69, 0x6e, 0x69, 0x77, 0x6e, 0x65, 0x6d, 0x2d, 0x72, 0x6e, 0x74, 0x6e, 0x40, 0x73, 0x2d, 0x74, 0x74, 0x65, 0x72, 0x2d, 0x2d, 0x69, 0x73, 0x70,
        0x69, 0x6c, 0x72, 0x76, 0x6d, 0x74, 0x6e, 0x73, 0x74, 0x72, 0x65, 0x63, 0x69, 0x65, 0x65, 0x72, 0x6f, 0x6e, 0x72, 0x72, 0x6c, 0x6e, 0x6e, 0x65, 0x6d, 0x74, 0x6c, 0x74, 0x65, 0x69, 0x2d, 0x6f, 0x69, 0x2e, 0x6e, 0x63, 0x65, 0x6c, 0x40, 0x70, 0x2d, 0x2d, 0x74, 0x73, 0x74, 0x40, 0x72, 0x74, 0x6c, 0x72, 0x6e, 0x6f, 0x73, 0x65, 0x74, 0x6d, 0x69, 0x32, 0x72, 0x65, 0x77, 0x6e, 0x76, 0x74, 0x73, 0x2d, 0x72, 0x6e, 0x69, 0x73, 0x40, 0x36, 0x2d, 0x6d, 0x2e, 0x65, 0x6d, 0x40, 0x69, 0x72, 0x72, 0x70, 0x65, 0x72, 0x76, 0x6c, 0x65, 0x76, 0x72, 0x65, 0x69, 0x65, 0x69, 0x6e, 0x72, 0x2d, 0x63, 0x72, 0x69, 0x6e, 0x72, 0x69, 0x6e, 0x69, 0x70, 0x6e, 0x2d, 0x69, 0x6c, 0x72, 0x2d, 0x65, 0x2d, 0x72, 0x6f, 0x65, 0x6e, 0x76, 0x6e, 0x40, 0x2d, 0x65, 0x72, 0x72, 0x6f, 0x6f, 0x72, 0x6c, 0x65, 0x74, 0x73, 0x72, 0x70, 0x77, 0x69, 0x69, 0x6d, 0x6c, 0x6d, 0x6e, 0x2d, 0x65, 0x65, 0x65, 0x74, 0x6c, 0x2d, 0x74, 0x6f, 0x2d, 0x74, 0x70, 0x72, 0x6e, 0x73, 0x72, 0x69, 0x72, 0x2e, 0x6d, 0x69, 0x65, 0x65, 0x32, 0x70, 0x6c, 0x6c, 0x65, 0x77, 0x2d, 0x72, 0x6f, 0x70, 0x76, 0x65, 0x2d, 0x72, 0x69, 0x6d, 0x72, 0x36, 0x40, 0x6d, 0x72, 0x6c, 0x6d, 0x77, 0x6c, 0x6e, 0x69, 0x72, 0x6d, 0x76, 0x73, 0x2e, 0x73, 0x72, 0x77, 0x73, 0x76, 0x2d, 0x73, 0x76, 0x6d, 0x76, 0x65, 0x69, 0x76, 0x63, 0x65, 0x72, 0x31, 0x72, 0x69, 0x76, 0x72, 0x65, 0x65, 0x2d, 0x73, 0x6d, 0x31, 0x72, 0x6e, 0x72, 0x2d, 0x2d, 0x36, 0x72, 0x73, 0x77, 0x2d, 0x77, 0x36, 0x76, 0x72, 0x6d, 0x65, 0x2d, 0x72, 0x70, 0x2d, 0x74, 0x32, 0x6c, 0x63, 0x6d, 0x6f, 0x6e, 0x2e, 0x2d, 0x69, 0x65, 0x73, 0x6d, 0x65, 0x73, 0x6e, 0x6d, 0x6c, 0x65, 0x6e, 0x72, 0x72, 0x72, 0x32, 0x70, 0x65, 0x73, 0x6c, 0x6d, 0x70, 0x6d, 0x72, 0x6f, 0x65, 0x6c, 0x76, 0x73, 0x63, 0x73, 0x65, 0x6c, 0x2d, 0x6e, 0x72, 0x65, 0x65, 0x72, 0x2d, 0x70, 0x6d, 0x69, 0x69, 0x65, 0x2d, 0x6c, 0x72, 0x69, 0x6c, 0x2d, 0x74, 0x65, 0x65, 0x69, 0x31, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x74, 0x73, 0x65, 0x32, 0x2d, 0x6f, 0x2d, 0x70, 0x72, 0x6f, 0x65, 0x69, 0x73, 0x32, 0x6d, 0x65, 0x2d, 0x65, 0x74, 0x6d, 0x6d, 0x73, 0x76, 0x6c, 0x69, 0x65, 0x2d, 0x73, 0x74, 0x65, 0x65, 0x72, 0x72, 0x74, 0x31, 0x2d, 0x76, 0x73, 0x2e, 0x2d, 0x2d, 0x72, 0x76, 0x77, 0x65, 0x72, 0x72, 0x40, 0x6e, 0x6c, 0x6d, 0x72, 0x74, 0x73, 0x72, 0x72, 0x65, 0x65, 0x2d, 0x6f, 0x74, 0x70, 0x63, 0xb8, 0xa1, 0x11, 0x6e, 0xd7, 0x74, 0x16, 0x7f, 0xb4, 0xba, 0x40, 0x93, 0x98, 0x00, 0x71, 0xcc, 0x42, 0xa7, 0x2f, 0x28, 0x69, 0xe7, 0x31, 0x48, 0x22, 0xa0, 0xe1, 0x45, 0xe3, 0xf7, 0x7f, 0x3a
    };
    uint8_t c2[] = {
        0x5b, 0x52, 0xf1, 0x2d, 0x94, 0xcb, 0xb0, 0x86, 0xd8, 0xd3, 0xe3, 0x20, 0x88, 0x47, 0xcf, 0x5a, 0x49, 0xd2, 0x11, 0x30, 0x92, 0x17, 0x8d, 0xf4, 0x99, 0xf7, 0x6c, 0x8a, 0xbc, 0xe7, 0x5c, 0x58, 0x6a, 0x65, 0xed, 0x81, 0xdc, 0xdd, 0xcf, 0x83, 0xcd, 0xa4, 0xed, 0xa2, 0x5e, 0x63, 0xd9, 0x98, 0xf6, 0x2e, 0x15, 0x76, 0x9a, 0xc8, 0x8c, 0x42, 0x54, 0x44, 0xf4, 0x47, 0xf5, 0x96, 0xc9, 0x6e, 0x23, 0x09, 0x1a, 0x0d, 0xe3, 0x04, 0xe6, 0xed, 0x48, 0x49, 0x62, 0x31, 0xe8, 0x36, 0x04, 0xed, 0xb9, 0xe7, 0xa6, 0x35, 0x4d, 0xcd, 0xe3, 0xfa, 0xa0, 0xc8, 0x34, 0xbd, 0x62, 0x7b, 0xbc, 0xbe, 0x1c, 0x5b, 0x69, 0x1f, 0x9c, 0x30, 0x20, 0x48, 0x52, 0xd1, 0xb6, 0x5e, 0xa2, 0x6e, 0x06, 0x94, 0x72, 0x10, 0x56, 0x7c, 0x94, 0xa5, 0xc0, 0xaa, 0xea, 0x48, 0x61, 0x03, 0x14, 0x94, 0x09, 0x77, 0xd9, 0xa7, 0xfe, 0x78, 0x17, 0x95, 0x4f, 0x7e, 0xb0, 0x32, 0x63, 0x02, 0x17, 0x47, 0x1e, 0x7d, 0xb2, 0x7d, 0xb5, 0xcb, 0x9f, 0x61, 0x65, 0xed, 0x03, 0xd2, 0xdb, 0xd1, 0xb3, 0xd6, 0x1a, 0xf5, 0x67, 0x0b, 0x8b, 0x6b, 0x44, 0xf2, 0x62, 0x42, 0xc2, 0x4d, 0xe1, 0x5c, 0xfe, 0xc6, 0x19, 0x2b, 0xfb, 0x03, 0x0f, 0x1b, 0x89, 0x08, 0x86, 0x40, 0xca, 0x45, 0x15, 0xda, 0x65, 0xcc, 0x73, 0x00, 0x49, 0x4e, 0x48, 0x21, 0x25, 0xc6, 0xde, 0x26, 0x21, 0x1d, 0xea, 0x3c, 0x11, 0xac, 0xef, 0x34, 0x4c, 0x96, 0xcc, 0x5e, 0x26, 0xf3, 0xcd, 0x70, 0x0d, 0x62, 0xea, 0x09, 0x35, 0x2b, 0x1e, 0x60, 0xe4, 0x76, 0xd3, 0x65, 0x01, 0x8c, 0xab, 0xd4, 0x89, 0xad, 0x81, 0x9d, 0x04, 0x01, 0xd5, 0x55, 0x3c, 0xcb, 0x32, 0xe1, 0xb5, 0xd4, 0xda, 0xb4, 0xa9, 0x01, 0xb2, 0x10, 0xc7, 0xb1, 0xa9, 0x54, 0x66, 0x1d, 0xcc, 0xff, 0x54, 0x0b, 0x84, 0x37, 0xe0, 0x3a, 0xa5, 0x68, 0x80, 0x87, 0xbc, 0x3c, 0x0f, 0xda, 0x7e, 0x3c, 0x23, 0xfc, 0xd8, 0xc5, 0x52, 0xf7, 0x22, 0x12, 0x05, 0x9c, 0x68, 0x39, 0xb1, 0xed, 0x26, 0x24, 0x2b, 0x7e, 0x0b, 0xaf, 0x9e, 0x97, 0x45, 0x7b, 0xa9, 0xbc, 0x48, 0x0e, 0x66, 0x93, 0x32, 0x0d, 0x6b, 0xd6, 0xf0, 0x4f, 0x54, 0x18, 0xcd, 0xc9, 0x8c, 0xce, 0xc4, 0xa2, 0xff, 0x1e, 0x69, 0x17, 0x7e, 0xf4, 0x99, 0x09, 0x68, 0xa1, 0x9e, 0x1f, 0xbf, 0x90, 0xdc, 0x77, 0x5d, 0x50, 0x2b, 0x0e, 0xff, 0x96, 0xdc, 0x21, 0x2e, 0x74, 0x22, 0x28, 0x88, 0xa0, 0x00, 0x32, 0x15, 0xb0, 0xfd, 0xb1, 0xc9, 0x75, 0xb3, 0x3c, 0xbd, 0x89, 0xc5, 0xa4, 0x48, 0x17, 0xa9, 0xc9, 0x50, 0x61, 0x0c, 0x35, 0x31, 0x55, 0x11, 0xe3, 0x23, 0xe9, 0x3e, 0x78, 0x25, 0xdc, 0x50, 0xe8, 0x23, 0x5f, 0xb7, 0x3f, 0xc7, 0xae, 0xf0, 0x82, 0x35, 0x46, 0x34, 0x63, 0xcc, 0x5d, 0x96, 0xb8, 0x6a, 0x7a, 0x7f, 0x54, 0x27, 0x1a, 0xa4, 0x63, 0xdd, 0xb0, 0xb6, 0x17, 0x08, 0xa1, 0x2e, 0x95, 0x9e, 0xd4, 0x9b, 0x71, 0x83, 0x81, 0x6c, 0xea, 0xab, 0x00, 0x2e, 0xca, 0x60, 0xc1, 0x4b, 0x83, 0xa7, 0xab, 0x47, 0xe8, 0x1b, 0x5a, 0x78, 0x4f, 0xec, 0xbd, 0x62, 0x94, 0x25, 0x75, 0x2e, 0x64, 0xe7, 0x70, 0x13, 0xac, 0xe9, 0x89, 0x4f, 0x1e, 0x79, 0xbc, 0x15, 0x0c, 0x8d, 0x40, 0xe8, 0x16, 0x31, 0x7c, 0xb8, 0xa5, 0xd7, 0x21, 0x39, 0x93, 0x9b, 0xe6, 0x05, 0x81, 0xb6, 0x20, 0xa8, 0x5d, 0x73, 0x58, 0x8b, 0x66, 0x92, 0xac, 0x23, 0xa0, 0xf4, 0x8c, 0xab, 0x58, 0xae, 0xb6, 0x9c, 0x3c, 0x4d, 0x77, 0x5f, 0xae, 0xe2, 0x57, 0x89, 0x8f, 0xe4, 0x68, 0x81, 0x24, 0x7d, 0x3b, 0x99, 0x46, 0x9f, 0x7b, 0x9d, 0xa6, 0xdd, 0x99, 0xcf, 0xc1, 0x79, 0x04, 0x95, 0xce, 0x96, 0x7a, 0xd9, 0xb5, 0x6e, 0xcf, 0xd1, 0x72, 0x18, 0x97, 0x76, 0xe2, 0xb7, 0x38, 0x1e, 0x24, 0x0b, 0x09, 0x00, 0x8b, 0x28, 0x5d, 0xf8, 0xd0, 0x50, 0x7f, 0xeb, 0x3b, 0x37, 0x61, 0x0b, 0xd3, 0xff, 0x65, 0x7d, 0x88, 0x1e, 0x1d, 0xbb, 0x6c, 0xf5, 0xf8, 0xf3, 0x2b, 0x51, 0xd9, 0x6d, 0xc9, 0xbe, 0xbe, 0xd1, 0x94, 0x0e, 0x58, 0x2a, 0x0a, 0xe4, 0xf8, 0x28, 0x26, 0xc3, 0x74, 0x87, 0xd3, 0x81, 0x48, 0x6e, 0x9b, 0xd5, 0xa1, 0x60, 0x87, 0xfc, 0x1b, 0x06, 0x33, 0x0d, 0x87, 0xfa, 0x9b, 0xf9, 0x73, 0x6b, 0x0c, 0xdf, 0xea, 0xee, 0x32, 0x78, 0xe0, 0xf8, 0x18, 0x3f, 0xc3, 0x3b, 0x12, 0x88, 0x0b, 0xb2, 0x4a, 0x52, 0x64, 0x4e, 0x58, 0x54, 0x82, 0x52, 0x61, 0x54, 0x28, 0x1b, 0xf7, 0x99, 0x06, 0xa2, 0xad, 0x04, 0x19, 0x9f, 0x2e, 0x34, 0xe6, 0xf0, 0xee, 0xeb, 0x93, 0x9a, 0x9c, 0x73, 0x86, 0x23, 0x6d, 0x5d, 0xae, 0x64, 0xec, 0x6f, 0xf9, 0x7c, 0xc7, 0x46, 0x96, 0xdb, 0x44, 0xf4, 0xab, 0xc9, 0x67, 0x61, 0xb8, 0xec, 0xf0, 0x99, 0xe0, 0x4d, 0x45, 0xed, 0xa3, 0x1c, 0xe9, 0x68, 0x31, 0x85, 0xa5, 0xa1, 0xba, 0x08, 0xdb, 0x3f, 0x84, 0x75, 0x70, 0x24, 0xcd, 0x49, 0xd4, 0x07, 0xa8, 0xaa, 0x52, 0xd9, 0x55, 0x68, 0x8f, 0x78, 0xd2, 0x5d, 0x46, 0x23, 0x60, 0x76, 0xe1, 0x22, 0xdc, 0x2a, 0xeb, 0xac, 0xbc, 0xeb, 0xd6, 0x4c, 0x0f, 0xb5, 0xcb, 0x47, 0xce, 0x43, 0x59, 0x1d, 0x3e, 0xfc, 0x7f, 0x7c, 0x93, 0x9e, 0xef, 0xcd, 0x79, 0x5c, 0x08, 0x8e, 0xeb, 0xa8, 0x98, 0x3e, 0x95, 0xd1, 0x36, 0x42, 0x57, 0xfd, 0x6d, 0xdc, 0xe0, 0xa3, 0x3f, 0x46, 0x32, 0xb7, 0xff, 0x00, 0x4f, 0x7b, 0x23, 0x4d, 0xd0, 0xe5, 0xdd, 0x40, 0xab, 0xb2, 0xcb, 0x45, 0x92, 0x76, 0x7c, 0x5b, 0x98, 0xc7, 0xc0, 0x54, 0x34, 0x94, 0x8e, 0xbb, 0x28, 0xcf, 0xba, 0xd9, 0xa0, 0xe6, 0xf3, 0x65, 0x61, 0xd7, 0x10, 0xd3, 0xeb, 0xce, 0x21, 0x6a, 0xca, 0x61, 0xe7, 0x81, 0x15, 0x18, 0x4e, 0x71, 0xb0, 0x99, 0x62, 0xd9, 0xeb, 0xd0, 0x8b, 0xe9, 0xdf, 0x6a, 0x6d, 0x59, 0x0b, 0x45, 0x93, 0x38, 0xfe, 0xe6, 0x6a, 0xd1, 0x5f, 0xb6, 0xe9, 0x86, 0x01, 0x38, 0xab, 0x59, 0x5c, 0xd7, 0xb7, 0xfa, 0x81, 0x8a, 0xbe, 0xdc, 0xeb, 0x50, 0x7d, 0x81, 0xfa, 0x1b, 0x8f, 0xce, 0x53, 0x38, 0xe4, 0x8a, 0x82, 0xbe, 0x7d, 0xdc, 0xd8, 0x57, 0x5a, 0x48, 0xa3, 0x38, 0x74, 0x8a, 0xac, 0xf2, 0xfd, 0xbf, 0xcc, 0xd8, 0x08, 0x4d, 0x3e, 0xae, 0xa9, 0x00, 0x66, 0x06, 0xcb, 0xf3,
        0x50, 0xcc, 0x52, 0xc7, 0x4b, 0x16, 0x33, 0xa5, 0xde, 0x20, 0xed, 0x6a, 0xa7, 0x58, 0x5e, 0x4e, 0x7e, 0x29, 0xab, 0xb9, 0x65, 0x9d, 0x17, 0xe0, 0x1e, 0x79, 0x77, 0xf6, 0x1e, 0xa0, 0xcb, 0x0c, 0xf7, 0xc0, 0xe4, 0xf6, 0x3b, 0x60, 0x81, 0xfe, 0xed, 0xd9, 0x42, 0xa9, 0x61, 0x9d, 0xa8, 0xd7, 0xe8, 0xaa, 0x97, 0xad, 0xbb, 0xba, 0x13, 0x6e, 0x05, 0xa5, 0xce, 0x7a, 0x65, 0x6f, 0x55, 0xe3, 0xcf, 0xbc, 0x67, 0x14, 0x64, 0x57, 0x9c, 0x46, 0x14, 0xd6, 0x1d, 0x39, 0x1c, 0xd3, 0xe8, 0x98, 0x20, 0x5a, 0x1a, 0x05, 0x3b, 0x27, 0xd5, 0x84, 0xca, 0xd4, 0x0b, 0xc4, 0x1e, 0xd8, 0x46, 0x29, 0x48, 0x95, 0xdb, 0xe5, 0x58, 0x8a, 0x51, 0xc7, 0x74, 0x7f, 0x53, 0xa8, 0xbb, 0x58, 0xc0, 0x5b, 0xe1, 0xa7, 0x27, 0x36, 0x6c, 0xa6, 0x70, 0xec, 0x88, 0xcd, 0x9a, 0x70, 0xe1, 0xa0, 0xc7, 0xdd, 0x60, 0x71, 0xf4, 0x2a, 0x51, 0x98, 0x8e, 0xab, 0xb8, 0x13, 0x03, 0x48, 0x5f, 0x44, 0xf8, 0x88, 0xd9, 0x7d, 0xd3, 0xf1, 0x5f, 0xc4, 0x2b, 0x44, 0x15, 0x57, 0x31, 0xa4, 0xa1, 0xdb, 0x6d, 0x2a, 0x5a, 0x5a, 0xf7, 0xde, 0xd5, 0x23, 0x38, 0x00, 0xe5, 0x5c, 0x55, 0xe7, 0x37, 0x9c, 0xcb, 0x8b, 0xc0, 0x33, 0x42, 0x68, 0x23, 0x84, 0x7d, 0x89, 0x9d, 0xae, 0x59, 0x18, 0xae, 0xea, 0x46, 0x3f, 0xac, 0x57, 0x0d, 0x5d, 0x49, 0x14, 0x50, 0xe5, 0x70, 0x17, 0x73, 0x09, 0x11, 0x93, 0x6b, 0x02, 0x22, 0xb7, 0x63, 0xc9, 0xe6, 0xa4, 0xe3, 0xb1, 0xf7, 0xa6, 0x58, 0x8d, 0x14, 0xa1, 0xda, 0x6a, 0xb9, 0x38, 0xf9, 0x20, 0x45, 0x8c, 0xe6, 0x32, 0x23, 0x9d, 0x5f, 0xba, 0xcb, 0xb4, 0x95, 0xf9, 0xa9, 0x5c, 0x60, 0x03, 0x5a, 0x8c, 0xa7, 0xb9, 0x65, 0xa8, 0x84, 0x38, 0xc0, 0x25, 0xe6, 0xa7, 0xc0, 0x3b, 0xbc, 0x11, 0xed, 0x0e, 0x9a, 0x6f, 0xfe, 0x61, 0x79, 0x86, 0x92, 0x3a, 0xce, 0xe0, 0xb7, 0x70, 0xad, 0xe0, 0xcc, 0x88, 0x47, 0xd9, 0x2a, 0x3d, 0x41, 0x06, 0x77, 0x41, 0xbe, 0x3f, 0x55, 0x31, 0x54, 0x10, 0x14, 0x5b, 0xdf, 0x88, 0xb2, 0x9f, 0xff, 0x11, 0xb8, 0x11, 0xdc, 0x5e, 0x64, 0xf9, 0x97, 0x8a, 0x26, 0x6a, 0x44, 0xb4, 0x83, 0x83, 0x9b, 0x81, 0xaa, 0xfd, 0xb5, 0x8b, 0x16, 0x18, 0x2e, 0x5c, 0xe4, 0x5b, 0x8f, 0xdd, 0x7c, 0x1f, 0x33, 0x2f, 0xef, 0x57, 0x8c, 0x6a, 0x3f, 0x3c, 0x19, 0x5e, 0x73, 0x64, 0xc5, 0xaf, 0x1d, 0xa1, 0xb4, 0x11, 0xee, 0x6b, 0x7e, 0x66, 0xfb, 0xaa, 0x03, 0x17, 0xe4, 0xc9, 0x90, 0x4b, 0xf2, 0x50, 0x55, 0x71, 0xad, 0x31, 0x71, 0x49, 0xd7, 0x80, 0xd1, 0xa5, 0x9f, 0x6d, 0x71, 0x28, 0x2b, 0x65, 0xcf, 0x8d, 0xb1, 0x2a, 0x33, 0xdc, 0x93, 0xff, 0x86, 0xd7, 0xa6, 0xd0, 0x46, 0x66, 0x32, 0x3d, 0x18, 0x8c, 0xd3, 0xda, 0xf6, 0x1b, 0xa0, 0x2d, 0x29, 0xfd, 0x8d, 0x57, 0x2c, 0x82, 0xed, 0x38, 0x4a, 0x6f, 0xc4, 0x3c, 0x9a, 0x61, 0xcb, 0xe5, 0xcf, 0xd3, 0x83, 0xa1, 0x91, 0x93, 0x0d, 0x75, 0xfd, 0x4e, 0x2c, 0x83, 0xa0, 0x85, 0x27, 0x13, 0x5a, 0x24, 0xbd, 0x08, 0x1e, 0xe9, 0xab, 0x92, 0x41, 0xc2, 0x3a, 0xa0, 0xe1, 0xfd, 0x00, 0xb9, 0xf8, 0xca, 0x0b, 0x1a, 0x8e, 0xf6, 0x27, 0x9f, 0x5a, 0xf0, 0x23, 0x07, 0xc8, 0xbf, 0xf6, 0x74, 0xe7, 0xf8, 0x67, 0xfc, 0x28, 0x4e, 0x6a, 0x6c, 0xc6, 0x83, 0xe3, 0xf0, 0x01, 0xe0, 0x0f, 0x2d, 0xdf, 0x9e, 0x4b, 0x8b, 0x06, 0x15, 0x4c, 0x9f, 0xdf, 0x55, 0x14, 0x44, 0xde, 0x34, 0x35, 0x5a, 0xcb, 0xe5, 0xa7, 0xb5, 0x7e, 0x00, 0x31, 0x98, 0x5f, 0x51, 0x11, 0x37, 0xe1, 0xd2, 0x99, 0x8f, 0x70, 0x13, 0x40, 0xa0, 0xbe, 0xf8, 0xde, 0xac, 0x37, 0x06, 0xb6, 0x26, 0xf3, 0xb1, 0x97, 0x0b, 0x85, 0x68, 0x09, 0xa4, 0xc8, 0x34, 0x0a, 0x41, 0x6e, 0xac, 0x1a, 0x5b, 0xe0, 0x91, 0x6f, 0xa3, 0x0a, 0xf6, 0x05, 0x37, 0x32, 0xe1, 0x8e, 0xd8, 0xed, 0x55, 0xa3, 0x54, 0x3f, 0x62, 0x95, 0x82, 0xcf, 0x0a, 0x19, 0xb4, 0x9f, 0x04, 0xcc, 0x86, 0x7e, 0xf1, 0xe5, 0x8b, 0x67, 0x73, 0xa2, 0x46, 0x4e, 0xf2, 0x98, 0x94, 0xb5, 0xeb, 0xa5, 0xbd, 0xcb, 0x66, 0x82, 0xe9, 0x87, 0xe9, 0xe3, 0x50, 0x55, 0x4b, 0xd6, 0x67, 0x30, 0xe1, 0x7c, 0x15, 0x77, 0x29, 0xfd, 0x85, 0x67, 0x5a, 0xc4, 0xd5, 0x69, 0xfa, 0xc7, 0x66, 0x66, 0x49, 0xf7, 0x5a, 0xcd, 0xd1, 0x81, 0x5c, 0x74, 0x8d, 0xbf, 0xc5, 0xc2, 0xff, 0x4d, 0x90, 0xe8, 0x8e, 0x05, 0x00, 0xff, 0x7a, 0xd7, 0xb2, 0x7a, 0xad, 0x8b, 0xd6, 0x4b, 0x52, 0x09, 0x50, 0x4b
    };

    if (true) {
        SrsHandshakeBytes bytes;
        ASSERT_EQ(ERROR_SUCCESS, bytes.create_c0c1());
        memcpy(bytes.c0c1, c0c1, 1537);
        ASSERT_EQ(ERROR_SUCCESS, bytes.create_s0s1s2());
        memcpy(bytes.s0s1s2, s0s1s2, 3073);
        ASSERT_EQ(ERROR_SUCCESS, bytes.create_c2());
        memcpy(bytes.c2, c2, 1536);
    
        MockEmptyIO eio;
        SrsSimpleHandshake hs;
        HELPER_EXPECT_SUCCESS(hs.handshake_with_client(&bytes, &eio));
        HELPER_EXPECT_SUCCESS(hs.handshake_with_server(&bytes, &eio));
    }

    if (true) {
        MockBufferIO io;
        io.append(s0s1s2, 3073);

        SrsRtmpClient r(&io);
        HELPER_EXPECT_SUCCESS(r.handshake());
    }

    if (true) {
        MockBufferIO io;
        io.append(s0s1s2, 3073);

        SrsRtmpClient r(&io);
        HELPER_EXPECT_SUCCESS(r.simple_handshake());
    }

    if (true) {
        MockBufferIO io;
        io.append(c0c1, 1537);
        io.append(c2, 1536);

        SrsRtmpServer r(&io);
        HELPER_EXPECT_SUCCESS(r.handshake());
    }
}

/**
* bytes equal utility
*/
VOID TEST(ProtocolHandshakeTest, BytesEqual)
{
    uint8_t a1[] = { 0x01 };
    uint8_t b1[] = { 0x02 };
    uint8_t a2[] = { 0x01, 0x02 };
    uint8_t b2[] = { 0x02, 0x03 };
    
    EXPECT_TRUE(srs_bytes_equals(NULL, NULL, 0));
    EXPECT_FALSE(srs_bytes_equals(a1, NULL, 1));
    EXPECT_FALSE(srs_bytes_equals(NULL, a1, 1));
    EXPECT_FALSE(srs_bytes_equals(a1, b1, 1));
    EXPECT_TRUE(srs_bytes_equals(a1, a1, 1));
    EXPECT_TRUE(srs_bytes_equals(a1, a2, 1));
    EXPECT_FALSE(srs_bytes_equals(a1, b2, 1));
}

/**
* generate tcUrl from ip/vhost/app/port
*/
VOID TEST(ProtocolUtilityTest, GenerateTcUrl)
{
    string ip; string vhost; string app; int port; string tcUrl; string param;
    
    ip = "127.0.0.1"; vhost = "__defaultVhost__"; app = "live"; port = 1935;
    tcUrl = srs_generate_tc_url(ip, vhost, app, port);
    EXPECT_STREQ("rtmp://127.0.0.1/live", tcUrl.c_str());
    
    ip = "127.0.0.1"; vhost = "demo"; app = "live"; port = 1935;
    tcUrl = srs_generate_tc_url(ip, vhost, app, port);
    EXPECT_STREQ("rtmp://demo/live", tcUrl.c_str());
    
    ip = "127.0.0.1"; vhost = "demo"; app = "live"; port = 19351;
    tcUrl = srs_generate_tc_url(ip, vhost, app, port);
    EXPECT_STREQ("rtmp://demo:19351/live", tcUrl.c_str());
}

/**
* shared ptr message array test
*/
VOID TEST(ProtocolMsgArrayTest, MessageArray)
{
    SrsMessageHeader header;
    SrsSharedPtrMessage msg;
    char* payload = new char[1024];
    EXPECT_TRUE(ERROR_SUCCESS == msg.create(&header, payload, 1024));
    EXPECT_EQ(0, msg.count());
    
    if (true) {
        SrsMessageArray arr(3);
        
        arr.msgs[0] = msg.copy();
        EXPECT_EQ(1, msg.count());
        
        arr.msgs[1] = msg.copy();
        EXPECT_EQ(2, msg.count());
        
        arr.msgs[2] = msg.copy();
        EXPECT_EQ(3, msg.count());
    }
    EXPECT_EQ(3, msg.count());
    
    if (true) {
        SrsMessageArray arr(3);
        
        arr.msgs[0] = msg.copy();
        EXPECT_EQ(4, msg.count());
        
        arr.msgs[2] = msg.copy();
        EXPECT_EQ(5, msg.count());
    }
    EXPECT_EQ(5, msg.count());
}

/**
* set/get timeout of protocol stack
*/
VOID TEST(ProtocolStackTest, ProtocolTimeout)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    EXPECT_TRUE(SRS_UTIME_NO_TIMEOUT == proto.get_recv_timeout());
    EXPECT_TRUE(SRS_UTIME_NO_TIMEOUT == proto.get_send_timeout());
    
    proto.set_recv_timeout(10 * SRS_UTIME_MILLISECONDS);
    EXPECT_TRUE(10 * SRS_UTIME_MILLISECONDS == proto.get_recv_timeout());
    
    proto.set_send_timeout(10 * SRS_UTIME_MILLISECONDS);
    EXPECT_TRUE(10 * SRS_UTIME_MILLISECONDS == proto.get_send_timeout());
}

/**
* get recv/send bytes of protocol stack.
*/
VOID TEST(ProtocolStackTest, ProtocolBytes)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    EXPECT_TRUE(0 == proto.get_recv_bytes());
    EXPECT_TRUE(0 == proto.get_send_bytes());
    
    SrsConnectAppPacket* pkt = new SrsConnectAppPacket();
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    EXPECT_TRUE(0 < proto.get_send_bytes());
}

/**
* recv a SrsConnectAppPacket packet.
*/
VOID TEST(ProtocolStackTest, ProtocolRecvMessage)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // packet is SrsConnectAppPacket
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0xa1, 0x14, 0x00, 0x00, 0x00, 0x00,
        // msg payload start
        0x02, 0x00, 0x07, 0x63,
        0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
        0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
        0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
        0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
        0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
        0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
        0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
        0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
        0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
        0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
        0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
        0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
        0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xC3, /*next chunk.*/         0x0d, 0x76, 0x69, 0x64,
        0x65, 0x6f, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x07, 0x70, 0x61, 0x67, 0x65, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x62, 0x68,
        0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72, 0x73, 0x2e,
        0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73,
        0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2e, 0x68, 0x74, 0x6d, 0x6c,
        0x3f, 0x76, 0x68, 0x6f, 0x73, 0x74, 0x3d, 0x64, 0x65, 0x76, 0x26, 0x73, 0x74, 0x72, 0x65, 0x61,
        0x6d, 0x3d, 0x6c, 0x69, 0x76, 0x65, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x26, 0x73, 0x65, 0x72,
        0x76, 0x65, 0x72, 0x3d, 0x64, 0x65, 0x76, 0x26, 0x70, 0x6f, 0x72, 0x74,
        0xC3, /*next chunk.*/         0x3d, 0x31, 0x39, 0x33,
        0x35, 0x00, 0x0e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e,
        0x67, 0x00, 0x40, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    
    SrsPacket* pkt = NULL;
    EXPECT_TRUE(ERROR_SUCCESS == proto.decode_message(msg, &pkt));
    SrsAutoFree(SrsPacket, pkt);
    
    SrsConnectAppPacket* spkt = dynamic_cast<SrsConnectAppPacket*>(pkt);
    ASSERT_TRUE(NULL != spkt);
}

// for librtmp, if ping, it will send a fresh stream with fmt=1,
// 0x42             where: fmt=1, cid=2, protocol contorl user-control message
// 0x00 0x00 0x00   where: timestamp=0
// 0x00 0x00 0x06   where: payload_length=6
// 0x04             where: message_type=4(protocol control user-control message)
// 0x00 0x06            where: event Ping(0x06)
// 0x00 0x00 0x0d 0x0f  where: event data 4bytes ping timestamp.
// @see: https://github.com/ossrs/srs/issues/98
VOID TEST(ProtocolStackTest, ProtocolRecvMessageBug98)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // packet is SrsConnectAppPacket
    uint8_t data[] = {
        0x42, // 1bytes chunk header
        0x00, 0x00, 0x00, // timestamp=0
        0x00, 0x00, 0x06, // payload_length=6
        0x04, // message_type=4(protocol control user-control message)
        0x00, 0x06, // event Ping(0x06)
        0x00, 0x00, 0x0d, 0x0f // event data 4bytes ping timestamp.
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    
    SrsPacket* pkt = NULL;
    EXPECT_TRUE(ERROR_SUCCESS == proto.decode_message(msg, &pkt));
    SrsAutoFree(SrsPacket, pkt);
    
    SrsUserControlPacket* spkt = dynamic_cast<SrsUserControlPacket*>(pkt);
    ASSERT_TRUE(NULL != spkt);
    EXPECT_EQ(SrcPCUCPingRequest, spkt->event_type);
    EXPECT_EQ(0x0d0f, spkt->event_data);
}

/**
* recv a SrsSetWindowAckSizePacket packet.
*/
VOID TEST(ProtocolStackTest, ProtocolRecvAckSizeMessage)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // packet is SrsSetWindowAckSizePacket
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x02,
        0x00, 0x00, 0x00,
        0x00, 0x00, 0x04,
        0x05,
        0x00, 0x00, 0x00, 0x00,
        // msg payload start
        0x00, 0x00, 0x07, 0x63
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    
    SrsPacket* pkt = NULL;
    EXPECT_TRUE(ERROR_SUCCESS == proto.decode_message(msg, &pkt));
    SrsAutoFree(SrsPacket, pkt);
    
    SrsSetWindowAckSizePacket* spkt = dynamic_cast<SrsSetWindowAckSizePacket*>(pkt);
    ASSERT_TRUE(NULL != spkt);
    EXPECT_EQ(0x0763, spkt->ackowledgement_window_size);
}

/**
* recv a video message.
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVMessage)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x03,
        0x00, 0x00, 0x00, // timestamp
        0x00, 0x00, 0x04, // length
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        // msg payload start
        0x00, 0x00, 0x07, 0x63
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
}

/**
* recv a audio message.
*/
VOID TEST(ProtocolStackTest, ProtocolRecvAMessage)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // audio message
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x03,
        0x00, 0x00, 0x00, // timestamp
        0x00, 0x00, 0x04, // length
        0x08, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        // msg payload start
        0x00, 0x00, 0x07, 0x63
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_audio());
}

/**
* recv a video message in 2 chunk packets.
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVMessage2Trunk)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x03,
        0x00, 0x00, 0x00, // timestamp
        0x00, 0x01, 0x10, // length, 272
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        // msg payload start
        0x02, 0x00, 0x07, 0x63,
        0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
        0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
        0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
        0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
        0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
        0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
        0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
        0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
        0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
        0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
        0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
        0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
        0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xC3, /*next chunk.*/
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
}

/**
* recv video and audio, interlaced in chunks.
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAMessage)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0x00, 0x00, 0x00, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x00, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x04,
            0x00, 0x00, 0x00, // timestamp
            0x00, 0x00, 0x90, // length, 144
            0x08, // message_type
            0x00, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC4, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_audio());
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
    }
}

/**
* recv video and audio, interlaced in chunks.
* the continue chunks use fmt=1 header
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAFmt1)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */
    
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0x00, 0x00, 0x00, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x00, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x04,
            0x00, 0x00, 0x00, // timestamp
            0x00, 0x00, 0x90, // length, 144
            0x08, // message_type
            0x00, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x43,
            0x00, 0x00, 0x00, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            // msg payload start
            /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC4, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_audio());
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
    }
}

/**
* recv video and audio, interlaced in chunks.
* the continue chunks use fmt=2 header
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAFmt2)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */
    
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0x00, 0x00, 0x00, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x00, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x04,
            0x00, 0x00, 0x00, // timestamp
            0x00, 0x00, 0x90, // length, 144
            0x08, // message_type
            0x00, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x83,
            0x00, 0x00, 0x00, // timestamp
            // msg payload start
            /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC4, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_audio());
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
    }
}

/**
* recv video and audio, interlaced in chunks.
* the continue chunks use fmt=3 header
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAFmt3)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */
    
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0x00, 0x00, 0x00, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x00, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x04,
            0x00, 0x00, 0x00, // timestamp
            0x00, 0x00, 0x90, // length, 144
            0x08, // message_type
            0x00, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0xC3,
            // msg payload start
            /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC4, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_audio());
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
    }
}

/**
* recv video, audio and video, interlaced in chunks.
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAVMessage)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0x00, 0x00, 0x10, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x04,
            0x00, 0x00, 0x15, // timestamp
            0x00, 0x00, 0x90, // length, 144
            0x08, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0x00, 0x00, 0x20, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC4, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x10, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_audio());
        EXPECT_EQ(0x15, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x20, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
}

/**
* recv video, audio and video, interlaced in chunks.
* the continue chunks use fmt=1 header
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAVFmt1)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */
    
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0x00, 0x00, 0x10, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x04,
            0x00, 0x00, 0x15, // timestamp
            0x00, 0x00, 0x90, // length, 144
            0x08, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x43,
            0x00, 0x00, 0x12, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC4, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x10, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_audio());
        EXPECT_EQ(0x15, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x22, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
}

/**
* recv video, audio and video, interlaced in chunks.
* the continue chunks use fmt=2 header
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAVFmt2)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */
    
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0x00, 0x00, 0x10, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x04,
            0x00, 0x00, 0x15, // timestamp
            0x00, 0x00, 0x90, // length, 144
            0x08, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x83,
            0x00, 0x00, 0x12, // timestamp
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC4, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x10, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_audio());
        EXPECT_EQ(0x15, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x22, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
}

/**
* recv video, audio and video, interlaced in chunks.
* the continue chunks use fmt=3 header
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAVFmt3)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */
    
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0x00, 0x00, 0x10, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x04,
            0x00, 0x00, 0x15, // timestamp
            0x00, 0x00, 0x90, // length, 144
            0x08, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0xC3,
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC4, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x10, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_audio());
        EXPECT_EQ(0x15, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x20, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
}

/**
* recv video, audio, video and video, interlaced in chunks.
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAVVMessage)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0x00, 0x00, 0x10, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x04,
            0x00, 0x00, 0x15, // timestamp
            0x00, 0x00, 0x90, // length, 144
            0x08, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0x00, 0x00, 0x20, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC4, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0x00, 0x00, 0x30, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x10, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_audio());
        EXPECT_EQ(0x15, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x20, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x30, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
}

/**
* recv video, audio, video and video, interlaced in chunks.
* the continue chunks use fmt=1 header
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAVVFmt1)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */
    
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0x00, 0x00, 0x10, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x04,
            0x00, 0x00, 0x15, // timestamp
            0x00, 0x00, 0x90, // length, 144
            0x08, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x43,
            0x00, 0x00, 0x10, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC4, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x43,
            0x00, 0x00, 0x10, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x10, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_audio());
        EXPECT_EQ(0x15, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x20, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x30, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
}

/**
* recv video, audio, video and video, interlaced in chunks.
* the continue chunks use fmt=2 header
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAVVFmt2)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */
    
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0x00, 0x00, 0x10, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x04,
            0x00, 0x00, 0x15, // timestamp
            0x00, 0x00, 0x90, // length, 144
            0x08, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x83,
            0x00, 0x00, 0x10, // timestamp
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC4, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x83,
            0x00, 0x00, 0x10, // timestamp
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x10, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_audio());
        EXPECT_EQ(0x15, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x20, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x30, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
}

/**
* recv video, audio, video and video, interlaced in chunks.
* the continue chunks use fmt=3 header
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAVVFmt3)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */
    
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0x00, 0x00, 0x10, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x04,
            0x00, 0x00, 0x15, // timestamp
            0x00, 0x00, 0x90, // length, 144
            0x08, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0xC3,
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC4, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0xC3,
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x10, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_audio());
        EXPECT_EQ(0x15, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x20, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x30, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
}

/**
* recv video, audio, video and video, interlaced in chunks.
* the continue chunks use fmt=1, last video with fmt=1 header
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAVVFmt11)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */
    
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0x00, 0x00, 0x10, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x04,
            0x00, 0x00, 0x15, // timestamp
            0x00, 0x00, 0x90, // length, 144
            0x08, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x43,
            0x00, 0x00, 0x10, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC4, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x43,
            0x00, 0x00, 0x20, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x10, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_audio());
        EXPECT_EQ(0x15, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x20, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x40, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
}

/**
* recv video, audio, video and video, interlaced in chunks.
* the continue chunks use fmt=1, last video with fmt=1 header,
* last video changed length
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAVVFmt11Length)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */
    
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0x00, 0x00, 0x10, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x04,
            0x00, 0x00, 0x15, // timestamp
            0x00, 0x00, 0x90, // length, 144
            0x08, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x43,
            0x00, 0x00, 0x10, // timestamp
            0x00, 0x01, 0x20, // length, 288
            0x09, // message_type
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC4, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x43,
            0x00, 0x00, 0x20, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x10, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_audio());
        EXPECT_EQ(0x15, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x20, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x40, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
}

/**
* recv video, audio, video and video, interlaced in chunks.
* the continue chunks use fmt=1, last video with fmt=2 header
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAVVFmt12)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */
    
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0x00, 0x00, 0x10, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x04,
            0x00, 0x00, 0x15, // timestamp
            0x00, 0x00, 0x90, // length, 144
            0x08, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x43,
            0x00, 0x00, 0x10, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC4, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x83,
            0x00, 0x00, 0x20, // timestamp
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x10, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_audio());
        EXPECT_EQ(0x15, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x20, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x40, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
}

/**
* recv video, audio, video and video, interlaced in chunks.
* the continue chunks use fmt=1, last video with fmt=2 header,
* last video changed length
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAVVFmt12Length)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */
    
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0x00, 0x00, 0x10, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x04,
            0x00, 0x00, 0x15, // timestamp
            0x00, 0x00, 0x90, // length, 144
            0x08, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x43,
            0x00, 0x00, 0x10, // timestamp
            0x00, 0x01, 0x20, // length, 288
            0x09, // message_type
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC4, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x83,
            0x00, 0x00, 0x20, // timestamp
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x10, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
        EXPECT_EQ(0x110, msg->header.payload_length);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_audio());
        EXPECT_EQ(0x15, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x20, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
        EXPECT_EQ(0x120, msg->header.payload_length);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x40, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
        EXPECT_EQ(0x120, msg->header.payload_length);
    }
}

/**
* recv video, with extended timestamp.
* small timestamp < 0xffffff
*/
VOID TEST(ProtocolStackTest, ProtocolRecvExtTimeMessage)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */
    
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x03,
        0xff, 0xff, 0xff, // timestamp
        0x00, 0x00, 0x04, // length
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        0x00, 0x00, 0x00, 0x10, // extended timestamp
        // msg payload start
        0x00, 0x00, 0x07, 0x63
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
    EXPECT_EQ(0x10, msg->header.timestamp);
}

/**
* recv video, with extended timestamp.
* big timestamp > 0xffffff
*/
VOID TEST(ProtocolStackTest, ProtocolRecvExtTimeMessage2)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */
    
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x03,
        0xff, 0xff, 0xff, // timestamp
        0x00, 0x00, 0x04, // length
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        0x7f, 0x01, 0x02, 0x03, // extended timestamp
        // msg payload start
        0x00, 0x00, 0x07, 0x63
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
    EXPECT_EQ(0x7f010203, msg->header.timestamp);
}

/**
* recv video, with extended timestamp.
* always use 31bits timestamp.
*/
// always use 31bits timestamp, for some server may use 32bits extended timestamp.
// @see https://github.com/ossrs/srs/issues/111
VOID TEST(ProtocolStackTest, ProtocolRecvExtTimeMessage3)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */
    
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x03,
        0xff, 0xff, 0xff, // timestamp
        0x00, 0x00, 0x04, // length
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        0xff, 0x01, 0x02, 0x03, // extended timestamp
        // msg payload start
        0x00, 0x00, 0x07, 0x63
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
    // always use 31bits timestamp
    EXPECT_EQ(0x7f010203, msg->header.timestamp);
}

/**
* recv video, with extended timestamp, in 2 chunks packet.
* always send extended timestamp in 0xCX chunk packets.
*/
/**
* RTMP specification and ffmpeg/librtmp is false,
* but, adobe changed the specification, so flash/FMLE/FMS always true.
* default to true to support flash/FMLE/FMS.
* 
* ffmpeg/librtmp may donot send this filed, need to detect the value.
* @see also: http://blog.csdn.net/win_lin/article/details/13363699
* compare to the chunk timestamp, which is set by chunk message header
* type 0,1 or 2.
*
* @remark, nginx send the extended-timestamp in sequence-header,
* and timestamp delta in continue C1 chunks, and so compatible with ffmpeg,
* that is, there is no continue chunks and extended-timestamp in nginx-rtmp.
*
* @remark, srs always send the extended-timestamp, to keep simple,
* and compatible with adobe products.
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVExtTime2Trunk)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x03,
        0xff, 0xff, 0xff, // timestamp
        0x00, 0x01, 0x10, // length, 272
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        0x00, 0x01, 0x02, 0x03, // extended timestamp
        // msg payload start
        0x02, 0x00, 0x07, 0x63,
        0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
        0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
        0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
        0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
        0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
        0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
        0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
        // chunk #2
        0xC3,
        0x00, 0x01, 0x02, 0x03, // extended timestamp
        /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
        0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
        0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
        0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
        0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
        0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        // chunk #2
        0xC3,
        0x00, 0x01, 0x02, 0x03, // extended timestamp
        /*next chunk.*/
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
    // 0xCX with extended timestamp.
    EXPECT_EQ(0x00010203, msg->header.timestamp);
}

/**
* recv video, with extended timestamp, in 2 chunks packet.
* never send extended timestamp in 0xCX chunk packets.
*/
// FFMPEG/librtmp, RTMP specification standard protocol.
VOID TEST(ProtocolStackTest, ProtocolRecvVExtTime2Trunk2)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x03,
        0xff, 0xff, 0xff, // timestamp
        0x00, 0x01, 0x10, // length, 272
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        0x00, 0x01, 0x02, 0x03, // extended timestamp
        // msg payload start
        0x02, 0x00, 0x07, 0x63,
        0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
        0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
        0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
        0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
        0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
        0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
        0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
        // chunk #2
        0xC3,
        /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
        0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
        0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
        0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
        0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
        0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        // chunk #2
        0xC3,
        /*next chunk.*/
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
    // 0xCX without extended timestamp.
    EXPECT_EQ(0x00010203, msg->header.timestamp);
}

/**
* a video message, in 2 chunks packet.
* use 1B chunk header, min chunk id is 2.
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVCid1BMin)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x02,
        0x00, 0x00, 0x00, // timestamp
        0x00, 0x01, 0x10, // length, 272
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        // msg payload start
        0x02, 0x00, 0x07, 0x63,
        0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
        0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
        0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
        0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
        0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
        0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
        0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
        // chunk #2
        0xC2, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
        0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
        0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
        0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
        0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
        0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        // chunk #2
        0xC2, /*next chunk.*/
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
    // 1B cid(6bits), min is 2
    EXPECT_EQ(0x02, msg->header.perfer_cid);
}

/**
* a video message, in 2 chunks packet.
* use 1B chunk header, cid in 2-63
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVCid1BNormal)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x09,
        0x00, 0x00, 0x00, // timestamp
        0x00, 0x01, 0x10, // length, 272
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        // msg payload start
        0x02, 0x00, 0x07, 0x63,
        0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
        0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
        0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
        0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
        0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
        0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
        0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
        // chunk #2
        0xC9, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
        0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
        0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
        0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
        0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
        0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        // chunk #2
        0xC9, /*next chunk.*/
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
    // 1B cid(6bits), cid in 2-63
    EXPECT_EQ(0x09, msg->header.perfer_cid);
}

/**
* a video message, in 2 chunks packet.
* use 1B chunk header, max chunk id is 63.
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVCid1BMax)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x3F,
        0x00, 0x00, 0x00, // timestamp
        0x00, 0x01, 0x10, // length, 272
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        // msg payload start
        0x02, 0x00, 0x07, 0x63,
        0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
        0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
        0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
        0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
        0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
        0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
        0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
        // chunk #2
        0xFF, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
        0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
        0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
        0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
        0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
        0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        // chunk #2
        0xFF, /*next chunk.*/
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
    // 1B cid(6bits), max is 63
    EXPECT_EQ(0x3F, msg->header.perfer_cid);
}

/**
* a video message, in 2 chunks packet.
* use 2B chunk header, min chunk id is 64.
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVCid2BMin)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x00, 0x00,
        0x00, 0x00, 0x00, // timestamp
        0x00, 0x01, 0x10, // length, 272
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        // msg payload start
        0x02, 0x00, 0x07, 0x63,
        0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
        0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
        0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
        0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
        0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
        0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
        0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
        // chunk #2
        0xC0, 0x00, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
        0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
        0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
        0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
        0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
        0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        // chunk #2
        0xC0, 0x00, /*next chunk.*/
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
    // 2B cid(8bits), min is 64
    EXPECT_EQ(64, msg->header.perfer_cid);
}

/**
* a video message, in 2 chunks packet.
* use 2B chunk header, cid in 64-319
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVCid2BNormal)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x00, 0x10,
        0x00, 0x00, 0x00, // timestamp
        0x00, 0x01, 0x10, // length, 272
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        // msg payload start
        0x02, 0x00, 0x07, 0x63,
        0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
        0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
        0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
        0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
        0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
        0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
        0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
        // chunk #2
        0xC0, 0x10, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
        0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
        0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
        0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
        0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
        0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        // chunk #2
        0xC0, 0x10, /*next chunk.*/
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
    // 2B cid(8bits), cid in 64-319
    EXPECT_EQ(0x10+64, msg->header.perfer_cid);
}

/**
* a video message, in 2 chunks packet.
* use 2B chunk header, cid in 64-319
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVCid2BNormal2)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x00, 0x11,
        0x00, 0x00, 0x00, // timestamp
        0x00, 0x01, 0x10, // length, 272
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        // msg payload start
        0x02, 0x00, 0x07, 0x63,
        0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
        0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
        0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
        0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
        0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
        0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
        0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
        // chunk #2
        0xC0, 0x11, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
        0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
        0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
        0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
        0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
        0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        // chunk #2
        0xC0, 0x11, /*next chunk.*/
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
    // 2B cid(8bits), cid in 64-319
    EXPECT_EQ(0x11+64, msg->header.perfer_cid);
}

/**
* a video message, in 2 chunks packet.
* use 2B chunk header, max chunk id is 319.
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVCid2BMax)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x00, 0xFF,
        0x00, 0x00, 0x00, // timestamp
        0x00, 0x01, 0x10, // length, 272
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        // msg payload start
        0x02, 0x00, 0x07, 0x63,
        0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
        0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
        0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
        0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
        0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
        0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
        0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
        // chunk #2
        0xC0, 0xFF, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
        0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
        0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
        0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
        0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
        0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        // chunk #2
        0xC0, 0xFF, /*next chunk.*/
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
    // 2B cid(68bits), max is 319
    EXPECT_EQ(319, msg->header.perfer_cid);
}

/**
* a video message, in 2 chunks packet.
* use 3B chunk header, min chunk id is 64.
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVCid3BMin)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x01, 0x00, 0x00,
        0x00, 0x00, 0x00, // timestamp
        0x00, 0x01, 0x10, // length, 272
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        // msg payload start
        0x02, 0x00, 0x07, 0x63,
        0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
        0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
        0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
        0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
        0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
        0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
        0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
        // chunk #2
        0xC1, 0x00, 0x00, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
        0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
        0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
        0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
        0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
        0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        // chunk #2
        0xC1, 0x00, 0x00, /*next chunk.*/
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
    // 3B cid(16bits), min is 64
    EXPECT_EQ(64, msg->header.perfer_cid);
}

/**
* a video message, in 2 chunks packet.
* use 3B chunk header, cid in 64-65599
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVCid3BNormal)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x01, 0x00, 0x10,
        0x00, 0x00, 0x00, // timestamp
        0x00, 0x01, 0x10, // length, 272
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        // msg payload start
        0x02, 0x00, 0x07, 0x63,
        0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
        0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
        0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
        0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
        0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
        0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
        0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
        // chunk #2
        0xC1, 0x00, 0x10, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
        0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
        0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
        0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
        0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
        0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        // chunk #2
        0xC1, 0x00, 0x10, /*next chunk.*/
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
    // 3B cid(16bits), cid in 64-65599
    EXPECT_EQ(0x10*256+64, msg->header.perfer_cid);
}

/**
* a video message, in 2 chunks packet.
* use 3B chunk header, cid in 64-65599, greater than 319
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVCid3BNormal2)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x01, 0x01, 0x10,
        0x00, 0x00, 0x00, // timestamp
        0x00, 0x01, 0x10, // length, 272
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        // msg payload start
        0x02, 0x00, 0x07, 0x63,
        0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
        0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
        0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
        0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
        0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
        0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
        0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
        // chunk #2
        0xC1, 0x01, 0x10, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
        0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
        0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
        0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
        0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
        0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        // chunk #2
        0xC1, 0x01, 0x10, /*next chunk.*/
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
    // 3B cid(16bits), cid in 64-65599
    EXPECT_EQ(0x01 + (0x10*256) + 64, msg->header.perfer_cid);
}

/**
* a video message, in 2 chunks packet.
* use 3B chunk header, cid in 64-65599, greater than 319
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVCid3BNormal3)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x01, 0xFF, 0x10,
        0x00, 0x00, 0x00, // timestamp
        0x00, 0x01, 0x10, // length, 272
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        // msg payload start
        0x02, 0x00, 0x07, 0x63,
        0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
        0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
        0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
        0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
        0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
        0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
        0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
        // chunk #2
        0xC1, 0xFF, 0x10, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
        0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
        0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
        0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
        0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
        0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        // chunk #2
        0xC1, 0xFF, 0x10, /*next chunk.*/
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
    // 3B cid(16bits), cid in 64-65599
    EXPECT_EQ(0xFF + (0x10*256) + 64, msg->header.perfer_cid);
}

/**
* a video message, in 2 chunks packet.
* use 3B chunk header, cid in 64-65599, greater than 319
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVCid3BNormal4)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x01, 0x02, 0x10,
        0x00, 0x00, 0x00, // timestamp
        0x00, 0x01, 0x10, // length, 272
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        // msg payload start
        0x02, 0x00, 0x07, 0x63,
        0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
        0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
        0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
        0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
        0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
        0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
        0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
        // chunk #2
        0xC1, 0x02, 0x10, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
        0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
        0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
        0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
        0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
        0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        // chunk #2
        0xC1, 0x02, 0x10, /*next chunk.*/
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
    // 3B cid(16bits), cid in 64-65599
    EXPECT_EQ(0x02 + (0x10*256) + 64, msg->header.perfer_cid);
}

/**
* a video message, in 2 chunks packet.
* use 3B chunk header, max chunk id is 65599.
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVCid3BMax)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x01, 0xFF, 0xFF,
        0x00, 0x00, 0x00, // timestamp
        0x00, 0x01, 0x10, // length, 272
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        // msg payload start
        0x02, 0x00, 0x07, 0x63,
        0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
        0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
        0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
        0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
        0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
        0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
        0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
        // chunk #2
        0xC1, 0xFF, 0xFF, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
        0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
        0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
        0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
        0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
        0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        // chunk #2
        0xC1, 0xFF, 0xFF, /*next chunk.*/
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
    // 2B cid(16bits), max is 65599
    EXPECT_EQ(65599, msg->header.perfer_cid);
}

/**
* recv a zero length video message.
*/
VOID TEST(ProtocolStackTest, ProtocolRecvV0LenMessage)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message
    uint8_t data[] = {
        // video #1
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x03,
        0x00, 0x00, 0x00, // timestamp
        0x00, 0x00, 0x00, // length
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        
        // video #2
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x03,
        0x00, 0x00, 0x00, // timestamp
        0x00, 0x00, 0x04, // length
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        // msg payload start
        0x00, 0x00, 0x07, 0x63
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
    // protocol stack will ignore the empty video message.
    EXPECT_EQ(4, msg->header.payload_length);
}

/**
* send a video message
*/
VOID TEST(ProtocolStackTest, ProtocolSendVMessage)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    uint8_t data[] = {0x01, 0x02, 0x03, 0x04};
    
    SrsCommonMessage* msg = new SrsCommonMessage();
    msg->size = sizeof(data);
    msg->payload = new char[msg->size];
    memcpy(msg->payload, data, msg->size);
    
    SrsSharedPtrMessage m;
    ASSERT_TRUE(ERROR_SUCCESS == m.create(msg));
    
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_message(m.copy(), 0));
    EXPECT_EQ(16, bio.out_buffer.length());
}

/**
* send a SrsCallPacket packet
*/
VOID TEST(ProtocolStackTest, ProtocolSendSrsCallPacket)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    SrsAmf0Object* args = SrsAmf0Any::object();
    
    SrsCallPacket* pkt = new SrsCallPacket();
    pkt->command_name = "my_call";
    pkt->command_object = SrsAmf0Any::null();
    pkt->arguments = args;
    
    args->set("video_id", SrsAmf0Any::number(100));
    args->set("url", SrsAmf0Any::str("http://ossrs.net/api/v1/videos/100"));
    args->set("date", SrsAmf0Any::str("2014-07-11 16:20:10.2984"));
    
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    char buf[] = {
        0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x14,
        0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x07, 0x6d,
        0x79, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
        0x03, 0x00, 0x08, 0x76, 0x69, 0x64, 0x65, 0x6f,
        0x5f, 0x69, 0x64, 0x00, 0x40, 0x59, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72,
        0x6c, 0x02, 0x00, 0x22, 0x68, 0x74, 0x74, 0x70,
        0x3a, 0x2f, 0x2f, 0x6f, 0x73, 0x73, 0x72, 0x73,
        0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x61, 0x70, 0x69,
        0x2f, 0x76, 0x31, 0x2f, 0x76, 0x69, 0x64, 0x65,
        0x6f, 0x73, 0x2f, 0x31, 0x30, 0x30, 0x00, 0x04,
        0x64, 0x61, 0x74, 0x65, 0x02, 0x00, 0x18, 0x32,
        0x30, 0x31, 0x34, 0x2d, 0x30, 0x37, 0x2d, 0x31,
        0x31, 0x20, 0x31, 0x36, 0x3a, 0x32, 0x30, 0x3a,
        0x31, 0x30, 0x2e, 0x32, 0x39, 0x38, 0x34, 0x00,
        0x00, 0x09
    };
    EXPECT_TRUE(srs_bytes_equals(bio.out_buffer.bytes(), buf, sizeof(buf)));
}

/**
* send a SrsCallResPacket packet
*/
VOID TEST(ProtocolStackTest, ProtocolSendSrsCallResPacket)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    SrsAmf0Object* args = SrsAmf0Any::object();
    
    SrsCallResPacket* pkt = new SrsCallResPacket(0);
    pkt->command_name = "_result";
    pkt->command_object = SrsAmf0Any::null();
    pkt->response = args;
    
    args->set("video_id", SrsAmf0Any::number(100));
    args->set("url", SrsAmf0Any::str("http://ossrs.net/api/v1/videos/100"));
    args->set("date", SrsAmf0Any::str("2014-07-11 16:20:10.2984"));
    
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    char buf[] = {
        0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x14,
        0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x07, 0x5f,
        0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
        0x03, 0x00, 0x08, 0x76, 0x69, 0x64, 0x65, 0x6f,
        0x5f, 0x69, 0x64, 0x00, 0x40, 0x59, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72,
        0x6c, 0x02, 0x00, 0x22, 0x68, 0x74, 0x74, 0x70,
        0x3a, 0x2f, 0x2f, 0x6f, 0x73, 0x73, 0x72, 0x73,
        0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x61, 0x70, 0x69,
        0x2f, 0x76, 0x31, 0x2f, 0x76, 0x69, 0x64, 0x65,
        0x6f, 0x73, 0x2f, 0x31, 0x30, 0x30, 0x00, 0x04,
        0x64, 0x61, 0x74, 0x65, 0x02, 0x00, 0x18, 0x32,
        0x30, 0x31, 0x34, 0x2d, 0x30, 0x37, 0x2d, 0x31,
        0x31, 0x20, 0x31, 0x36, 0x3a, 0x32, 0x30, 0x3a,
        0x31, 0x30, 0x2e, 0x32, 0x39, 0x38, 0x34, 0x00,
        0x00, 0x09
    };
    EXPECT_TRUE(srs_bytes_equals(bio.out_buffer.bytes(), buf, sizeof(buf)));
}

/**
* send a SrsCreateStreamPacket packet
*/
VOID TEST(ProtocolStackTest, ProtocolSendSrsCreateStreamPacket)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    SrsCreateStreamPacket* pkt = new SrsCreateStreamPacket();
    pkt->command_object = SrsAmf0Any::null();
    
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    char buf[] = {
        0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x14,
        0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0c, 0x63,
        0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x74, 0x72,
        0x65, 0x61, 0x6d, 0x00, 0x40, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x05
    };
    EXPECT_TRUE(srs_bytes_equals(bio.out_buffer.bytes(), buf, sizeof(buf)));
}

/**
* send a SrsFMLEStartPacket packet
*/
VOID TEST(ProtocolStackTest, ProtocolSendSrsFMLEStartPacket)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    SrsFMLEStartPacket* pkt = new SrsFMLEStartPacket();
    pkt->command_name = "FMLEStart";
    pkt->command_object = SrsAmf0Any::null();
    pkt->stream_name = "livestream";
    
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    char buf[] = {
        0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x14,
        0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x09, 0x46,
        0x4d, 0x4c, 0x45, 0x53, 0x74, 0x61, 0x72, 0x74,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x05, 0x02, 0x00, 0x0a, 0x6c, 0x69, 0x76,
        0x65, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d
    };
    EXPECT_TRUE(srs_bytes_equals(bio.out_buffer.bytes(), buf, sizeof(buf)));
}

/**
* send a SrsFMLEStartResPacket packet
*/
VOID TEST(ProtocolStackTest, ProtocolSendSrsFMLEStartResPacket)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    SrsAmf0Object* args = SrsAmf0Any::object();
    
    SrsFMLEStartResPacket* pkt = new SrsFMLEStartResPacket(1);
    pkt->command_name = "FMLEStart";
    pkt->command_object = SrsAmf0Any::null();
    pkt->args = args;
    
    args->set("stream" , SrsAmf0Any::str("livestream"));
    args->set("start" , SrsAmf0Any::number(0));
    
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    uint8_t buf[] = {
        0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x14,
        0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x09, 0x46,
        0x4d, 0x4c, 0x45, 0x53, 0x74, 0x61, 0x72, 0x74,
        0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x05, 0x06
    };
    EXPECT_TRUE(srs_bytes_equals(bio.out_buffer.bytes(), (char*)buf, sizeof(buf)));
}

/**
* send a SrsPublishPacket packet
*/
VOID TEST(ProtocolStackTest, ProtocolSendSrsPublishPacket)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    SrsPublishPacket* pkt = new SrsPublishPacket();
    pkt->command_name = "publish";
    pkt->command_object = SrsAmf0Any::null();
    pkt->stream_name = "livestream";
    pkt->type = "live";
    
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    uint8_t buf[] = {
        0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x14,
        0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x07, 0x70,
        0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
        0x02, 0x00, 0x0a, 0x6c, 0x69, 0x76, 0x65, 0x73,
        0x74, 0x72, 0x65, 0x61, 0x6d, 0x02, 0x00, 0x04,
        0x6c, 0x69, 0x76, 0x65
    };
    EXPECT_TRUE(srs_bytes_equals(bio.out_buffer.bytes(), (char*)buf, sizeof(buf)));
}

/**
* send a SrsPlayResPacket packet
*/
VOID TEST(ProtocolStackTest, ProtocolSendSrsPlayResPacket)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    SrsAmf0Object* args = SrsAmf0Any::object();
    
    SrsPlayResPacket* pkt = new SrsPlayResPacket();
    pkt->command_name = "_result";
    pkt->command_object = SrsAmf0Any::null();
    pkt->desc = args;
    
    args->set("stream" , SrsAmf0Any::str("livestream"));
    args->set("start" , SrsAmf0Any::number(0));
    
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    char buf[] = {
        0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x14,
        0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x07, 0x5f,
        0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
        0x03, 0x00, 0x06, 0x73, 0x74, 0x72, 0x65, 0x61,
        0x6d, 0x02, 0x00, 0x0a, 0x6c, 0x69, 0x76, 0x65,
        0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x00, 0x05,
        0x73, 0x74, 0x61, 0x72, 0x74, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x09
    };
    EXPECT_TRUE(srs_bytes_equals(bio.out_buffer.bytes(), buf, sizeof(buf)));
}

/**
* send a SrsOnBWDonePacket packet
*/
VOID TEST(ProtocolStackTest, ProtocolSendSrsOnBWDonePacket)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    SrsOnBWDonePacket* pkt = new SrsOnBWDonePacket();
    pkt->command_name = "onBWDone";
    pkt->args = SrsAmf0Any::null();
    
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    char buf[] = {
        0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x14,
        0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x08, 0x6f,
        0x6e, 0x42, 0x57, 0x44, 0x6f, 0x6e, 0x65, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x05
    };
    EXPECT_TRUE(srs_bytes_equals(bio.out_buffer.bytes(), buf, sizeof(buf)));
}

/**
* send a SrsOnStatusCallPacket packet
*/
VOID TEST(ProtocolStackTest, ProtocolSendSrsOnStatusCallPacket)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    SrsAmf0Object* args = SrsAmf0Any::object();
    
    args->set("stream" , SrsAmf0Any::str("livestream"));
    args->set("start" , SrsAmf0Any::number(0));
    
    SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket();
    pkt->command_name = "onStatus";
    pkt->args = SrsAmf0Any::null();
    pkt->data = args;
    
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    char buf[] = {
        0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x14,
        0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x08, 0x6f,
        0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x05, 0x03, 0x00, 0x06, 0x73, 0x74, 0x72, 0x65,
        0x61, 0x6d, 0x02, 0x00, 0x0a, 0x6c, 0x69, 0x76,
        0x65, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x00,
        0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x09
    };
    EXPECT_TRUE(srs_bytes_equals(bio.out_buffer.bytes(), buf, sizeof(buf)));
}

/**
* send a SrsBandwidthPacket packet
*/
VOID TEST(ProtocolStackTest, ProtocolSendSrsBandwidthPacket)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    SrsAmf0Object* args = SrsAmf0Any::object();
    
    args->set("stream" , SrsAmf0Any::str("livestream"));
    args->set("start" , SrsAmf0Any::number(0));
    
    SrsBandwidthPacket* pkt = new SrsBandwidthPacket();
    pkt->command_name = "startPublish";
    pkt->args = SrsAmf0Any::null();
    pkt->data = args;
    
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    char buf[] = {
        0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x14,
        0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0c, 0x73,
        0x74, 0x61, 0x72, 0x74, 0x50, 0x75, 0x62, 0x6c,
        0x69, 0x73, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x05, 0x03, 0x00, 0x06,
        0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x02, 0x00,
        0x0a, 0x6c, 0x69, 0x76, 0x65, 0x73, 0x74, 0x72,
        0x65, 0x61, 0x6d, 0x00, 0x05, 0x73, 0x74, 0x61,
        0x72, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x09
    };
    EXPECT_TRUE(srs_bytes_equals(bio.out_buffer.bytes(), buf, sizeof(buf)));
}

/**
* send a SrsOnStatusDataPacket packet
*/
VOID TEST(ProtocolStackTest, ProtocolSendSrsOnStatusDataPacket)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    SrsAmf0Object* args = SrsAmf0Any::object();
    
    args->set("stream" , SrsAmf0Any::str("livestream"));
    args->set("start" , SrsAmf0Any::number(0));
    
    SrsOnStatusDataPacket* pkt = new SrsOnStatusDataPacket();
    pkt->command_name = "onData";
    pkt->data = args;
    
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    char buf[] = {
        0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x12,
        0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x06, 0x6f,
        0x6e, 0x44, 0x61, 0x74, 0x61, 0x03, 0x00, 0x06,
        0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x02, 0x00,
        0x0a, 0x6c, 0x69, 0x76, 0x65, 0x73, 0x74, 0x72,
        0x65, 0x61, 0x6d, 0x00, 0x05, 0x73, 0x74, 0x61,
        0x72, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x09
    };
    EXPECT_TRUE(srs_bytes_equals(bio.out_buffer.bytes(), buf, sizeof(buf)));
}

/**
* send a SrsSampleAccessPacket packet
*/
VOID TEST(ProtocolStackTest, ProtocolSendSrsSampleAccessPacket)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    SrsSampleAccessPacket* pkt = new SrsSampleAccessPacket();
    pkt->command_name = "|RtmpSampleAccess";
    pkt->video_sample_access = true;
    pkt->audio_sample_access = true;
    
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    char buf[] = {
        0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x12,
        0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x11, 0x7c,
        0x52, 0x74, 0x6d, 0x70, 0x53, 0x61, 0x6d, 0x70,
        0x6c, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73,
        0x01, 0x01, 0x01, 0x01
    };
    EXPECT_TRUE(srs_bytes_equals(bio.out_buffer.bytes(), buf, sizeof(buf)));
}

/**
* send a SrsOnMetaDataPacket packet
*/
VOID TEST(ProtocolStackTest, ProtocolSendSrsOnMetaDataPacket)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    SrsAmf0Object* args = SrsAmf0Any::object();
    
    args->set("width" , SrsAmf0Any::number(1024));
    args->set("height" , SrsAmf0Any::number(576));
    
    SrsOnMetaDataPacket* pkt = new SrsOnMetaDataPacket();
    pkt->name = "onMetaData";
    pkt->metadata = args;
    
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    uint8_t buf[] = {
        0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x12,
        0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0a, 0x6f,
        0x6e, 0x4d, 0x65, 0x74, 0x61, 0x44, 0x61, 0x74,
        0x61, 0x03, 0x00, 0x05, 0x77, 0x69, 0x64, 0x74,
        0x68, 0x00, 0x40, 0x90, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x06, 0x68, 0x65, 0x69, 0x67,
        0x68, 0x74, 0x00, 0x40, 0x82, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x09
    };
    EXPECT_TRUE(srs_bytes_equals(bio.out_buffer.bytes(), (char*)buf, sizeof(buf)));
}

/**
* send a SrsSetWindowAckSizePacket packet
*/
VOID TEST(ProtocolStackTest, ProtocolSendSrsSetWindowAckSizePacket)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    SrsSetWindowAckSizePacket* pkt = new SrsSetWindowAckSizePacket();
    pkt->ackowledgement_window_size = 102400;
    
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    uint8_t buf[] = {
        0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x05,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x90, 0x00
    };
    EXPECT_TRUE(srs_bytes_equals(bio.out_buffer.bytes(), (char*)buf, sizeof(buf)));
}

/**
* send a SrsAcknowledgementPacket packet
*/
VOID TEST(ProtocolStackTest, ProtocolSendSrsAcknowledgementPacket)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    SrsAcknowledgementPacket* pkt = new SrsAcknowledgementPacket();
    pkt->sequence_number = 1024;
    
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    char buf[] = {
        0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x03,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00
    };
    EXPECT_TRUE(srs_bytes_equals(bio.out_buffer.bytes(), buf, sizeof(buf)));
}

/**
* send a SrsSetChunkSizePacket packet
*/
VOID TEST(ProtocolStackTest, ProtocolSendSrsSetChunkSizePacket)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    SrsSetChunkSizePacket* pkt = new SrsSetChunkSizePacket();
    pkt->chunk_size = 1024;
    
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    char buf[] = {
        0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00
    };
    EXPECT_TRUE(srs_bytes_equals(bio.out_buffer.bytes(), buf, sizeof(buf)));
}

/**
* send a SrsSetPeerBandwidthPacket packet
*/
VOID TEST(ProtocolStackTest, ProtocolSendSrsSetPeerBandwidthPacket)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    SrsSetPeerBandwidthPacket* pkt = new SrsSetPeerBandwidthPacket();
    pkt->type = SrsPeerBandwidthSoft;
    pkt->bandwidth = 1024;
    
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    char buf[] = {
        0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x06,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
        0x01
    };
    EXPECT_TRUE(srs_bytes_equals(bio.out_buffer.bytes(), buf, sizeof(buf)));
}

/**
* send a SrsUserControlPacket packet
*/
VOID TEST(ProtocolStackTest, ProtocolSendSrsUserControlPacket)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    SrsUserControlPacket* pkt = new SrsUserControlPacket();
    pkt->event_type = SrcPCUCSetBufferLength;
    pkt->event_data = 0x01;
    pkt->extra_data = 0x10;
    
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    char buf[] = {
        0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x04,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
        0x00, 0x01, 0x00, 0x00, 0x00, 0x10
    };
    
    EXPECT_TRUE(srs_bytes_equals(bio.out_buffer.bytes(), buf, sizeof(buf)));
    EXPECT_TRUE(true);
}

/**
* recv video message from multiple chunks,
* the next chunks must not be fmt=0 which means new msg.
*/
// when exists cache msg, means got an partial message,
// the fmt must not be type0 which means new message.
VOID TEST(ProtocolStackTest, ProtocolRecvVMessageFmtInvalid)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x03,
        0x00, 0x00, 0x00, // timestamp
        0x00, 0x01, 0x10, // length, 272
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        // msg payload start
        0x02, 0x00, 0x07, 0x63,
        0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
        0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
        0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
        0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
        0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
        0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
        0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
        0x03, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
        0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
        0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
        0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
        0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
        0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xC3, /*next chunk.*/
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    EXPECT_FALSE(ERROR_SUCCESS == proto.recv_message(&msg));
}

/**
* when recv bytes archive the ack-size, server must response a acked-size auto.
*/
VOID TEST(ProtocolStackTest, ProtocolAckSizeFlow)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    if (true) {
        SrsSetWindowAckSizePacket* pkt = new SrsSetWindowAckSizePacket();
        pkt->ackowledgement_window_size = 512;
        
        EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    }
    
    if (true) {
        SrsCommonMessage* msg = new SrsCommonMessage();
        msg->header.payload_length = msg->size = 4096;
        msg->payload = new char[msg->size];
        
        msg->header.message_type = 9;
        EXPECT_TRUE(msg->header.is_video());
    
        SrsSharedPtrMessage m;
        ASSERT_TRUE(ERROR_SUCCESS == m.create(msg));

        EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_message(m.copy(), 1));
    }
    
    // copy output to input
    if (true) {
        bio.in_buffer.append(bio.out_buffer.bytes(), bio.out_buffer.length());
        bio.out_buffer.erase(bio.out_buffer.length());
    }
    
    // recv SrsSetWindowAckSizePacket
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        ASSERT_TRUE(msg->header.is_window_ackledgement_size());
    }
    // recv video
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        ASSERT_TRUE(msg->header.is_video());
    }
    
    // copy output to input
    if (true) {
        bio.in_buffer.append(bio.out_buffer.bytes(), bio.out_buffer.length());
        bio.out_buffer.erase(bio.out_buffer.length());
    }
    // recv auto send acked size. #1
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        ASSERT_TRUE(msg->header.is_ackledgement());
    }
    
    // send again
    if (true) {
        SrsCommonMessage* msg = new SrsCommonMessage();
        msg->header.payload_length = msg->size = 4096;
        msg->payload = new char[msg->size];
        
        msg->header.message_type = 9;
        EXPECT_TRUE(msg->header.is_video());
    
        SrsSharedPtrMessage m;
        ASSERT_TRUE(ERROR_SUCCESS == m.create(msg));

        EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_message(m.copy(), 1));
    }
    // copy output to input
    if (true) {
        bio.in_buffer.append(bio.out_buffer.bytes(), bio.out_buffer.length());
        bio.out_buffer.erase(bio.out_buffer.length());
    }
    // recv video
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        ASSERT_TRUE(msg->header.is_video());
    }
    
    // copy output to input
    if (true) {
        bio.in_buffer.append(bio.out_buffer.bytes(), bio.out_buffer.length());
        bio.out_buffer.erase(bio.out_buffer.length());
    }
    // recv auto send acked size. #2
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        ASSERT_TRUE(msg->header.is_ackledgement());
    }
}

/**
* when recv ping message, server will response it auto.
*/
VOID TEST(ProtocolStackTest, ProtocolPingFlow)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // ping request
    if (true) {
        SrsUserControlPacket* pkt = new SrsUserControlPacket();
        pkt->event_type = SrcPCUCPingRequest;
        pkt->event_data = 0x3456;
        EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    }
    // copy output to input
    if (true) {
        bio.in_buffer.append(bio.out_buffer.bytes(), bio.out_buffer.length());
        bio.out_buffer.erase(bio.out_buffer.length());
    }
    // recv ping
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_user_control_message());
    }
    
    // recv the server auto send ping response message
    // copy output to input
    if (true) {
        bio.in_buffer.append(bio.out_buffer.bytes(), bio.out_buffer.length());
        bio.out_buffer.erase(bio.out_buffer.length());
    }
    // recv ping
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        ASSERT_TRUE(msg->header.is_user_control_message());
        
        SrsPacket* pkt = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.decode_message(msg, &pkt));
        SrsUserControlPacket* spkt = dynamic_cast<SrsUserControlPacket*>(pkt);
        ASSERT_TRUE(spkt != NULL);
        
        EXPECT_TRUE(SrcPCUCPingResponse == spkt->event_type);
        EXPECT_TRUE(0x3456 == spkt->event_data);
    }
}

/**
* expect specified message
*/
VOID TEST(ProtocolStackTest, ProtocolExcpectMessage)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // packet is SrsConnectAppPacket
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0xa1, 0x14, 0x00, 0x00, 0x00, 0x00,
        // msg payload start
        0x02, 0x00, 0x07, 0x63,
        0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
        0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
        0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
        0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
        0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
        0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
        0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
        0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
        0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
        0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
        0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
        0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
        0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xC3, /*next chunk.*/         0x0d, 0x76, 0x69, 0x64,
        0x65, 0x6f, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x07, 0x70, 0x61, 0x67, 0x65, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x62, 0x68,
        0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72, 0x73, 0x2e,
        0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73,
        0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2e, 0x68, 0x74, 0x6d, 0x6c,
        0x3f, 0x76, 0x68, 0x6f, 0x73, 0x74, 0x3d, 0x64, 0x65, 0x76, 0x26, 0x73, 0x74, 0x72, 0x65, 0x61,
        0x6d, 0x3d, 0x6c, 0x69, 0x76, 0x65, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x26, 0x73, 0x65, 0x72,
        0x76, 0x65, 0x72, 0x3d, 0x64, 0x65, 0x76, 0x26, 0x70, 0x6f, 0x72, 0x74,
        0xC3, /*next chunk.*/         0x3d, 0x31, 0x39, 0x33,
        0x35, 0x00, 0x0e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e,
        0x67, 0x00, 0x40, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    SrsConnectAppPacket* pkt = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.expect_message<SrsConnectAppPacket>(&msg, &pkt));
    SrsAutoFree(SrsCommonMessage, msg);
    SrsAutoFree(SrsConnectAppPacket, pkt);
    ASSERT_TRUE(NULL != pkt);
}

VOID TEST(ProtocolRTMPTest, RTMPRequest)
{
    SrsRequest req;
    std::string param;
    
    req.stream = "livestream";
    srs_discovery_tc_url("rtmp://std.ossrs.net/live", 
        req.schema, req.host, req.vhost, req.app, req.stream, req.port, param);
    req.strip();
    EXPECT_STREQ("rtmp", req.schema.c_str());
    EXPECT_STREQ("std.ossrs.net", req.host.c_str());
    EXPECT_STREQ("std.ossrs.net", req.vhost.c_str());
    EXPECT_STREQ("live", req.app.c_str());
    EXPECT_EQ(1935, req.port);
    
    req.stream = "livestream";
    srs_discovery_tc_url("rtmp://s td.os srs.n et/li v e", 
        req.schema, req.host, req.vhost, req.app, req.stream, req.port, param);
    req.strip();
    EXPECT_STREQ("rtmp", req.schema.c_str());
    EXPECT_STREQ("std.ossrs.net", req.host.c_str());
    EXPECT_STREQ("std.ossrs.net", req.vhost.c_str());
    EXPECT_STREQ("live", req.app.c_str());
    EXPECT_EQ(1935, req.port);
    
    req.stream = "livestream";
    srs_discovery_tc_url("rtmp://s\ntd.o\rssrs.ne\nt/li\nve", 
        req.schema, req.host, req.vhost, req.app, req.stream, req.port, param);
    req.strip();
    EXPECT_STREQ("rtmp", req.schema.c_str());
    EXPECT_STREQ("std.ossrs.net", req.host.c_str());
    EXPECT_STREQ("std.ossrs.net", req.vhost.c_str());
    EXPECT_STREQ("live", req.app.c_str());
    EXPECT_EQ(1935, req.port);
    
    req.stream = "livestream";
    srs_discovery_tc_url("rtmp://std.ossrs.net/live ", 
        req.schema, req.host, req.vhost, req.app, req.stream, req.port, param);
    req.strip();
    EXPECT_STREQ("rtmp", req.schema.c_str());
    EXPECT_STREQ("std.ossrs.net", req.host.c_str());
    EXPECT_STREQ("std.ossrs.net", req.vhost.c_str());
    EXPECT_STREQ("live", req.app.c_str());
    EXPECT_EQ(1935, req.port);
    
    EXPECT_TRUE(NULL == req.args);
    SrsRequest req1;
    req1.args = SrsAmf0Any::object();
    req.update_auth(&req1);
    EXPECT_TRUE(NULL != req.args);
    EXPECT_TRUE(req1.args != req.args);
}

VOID TEST(ProtocolRTMPTest, RTMPHandshakeBytes)
{
    MockBufferIO bio;
    SrsHandshakeBytes bytes;
    
    char hs[3073];
    bio.in_buffer.append(hs, sizeof(hs));
    bio.in_buffer.append(hs, sizeof(hs));
    
    EXPECT_TRUE(ERROR_SUCCESS == bytes.read_c0c1(&bio));
    EXPECT_TRUE(bytes.c0c1 != NULL);
    
    EXPECT_TRUE(ERROR_SUCCESS == bytes.read_c2(&bio));
    EXPECT_TRUE(bytes.c2 != NULL);
    
    EXPECT_TRUE(ERROR_SUCCESS == bytes.read_s0s1s2(&bio));
    EXPECT_TRUE(bytes.s0s1s2 != NULL);
}

struct MockStage
{
	http_parser parser;
	const char* at;
	size_t length;

	MockStage(http_parser* from);
};

MockStage::MockStage(http_parser* from)
{
	parser = *from;
	at = NULL;
	length = 0;
}

class MockParser
{
private:
	http_parser_settings settings;
	http_parser* parser;
	size_t parsed;
public:
	MockStage* message_begin;
	MockStage* url;
	MockStage* status;
	MockStage* header_field;
	MockStage* header_value;
	MockStage* headers_complete;
	MockStage* body;
	MockStage* message_complete;
	MockStage* chunk_header;
	MockStage* chunk_complete;
public:
	MockParser();
	virtual ~MockParser();
public:
	srs_error_t parse(string data);
private:
    static int on_message_begin(http_parser* parser);
    static int on_url(http_parser* parser, const char* at, size_t length);
    static int on_status(http_parser* parser, const char* at, size_t length);
    static int on_header_field(http_parser* parser, const char* at, size_t length);
    static int on_header_value(http_parser* parser, const char* at, size_t length);
    static int on_headers_complete(http_parser* parser);
    static int on_body(http_parser* parser, const char* at, size_t length);
    static int on_message_complete(http_parser* parser);
    static int on_chunk_header(http_parser* parser);
    static int on_chunk_complete(http_parser* parser);
};

MockParser::MockParser()
{
	parser = new http_parser();
	http_parser_init(parser, HTTP_REQUEST);
    parser->data = (void*)this;
    parsed = 0;

    memset(&settings, 0, sizeof(settings));
    settings.on_message_begin = on_message_begin;
    settings.on_url = on_url;
    settings.on_status = on_status;
    settings.on_header_field = on_header_field;
    settings.on_header_value = on_header_value;
    settings.on_headers_complete = on_headers_complete;
    settings.on_body = on_body;
    settings.on_message_complete = on_message_complete;
    settings.on_chunk_header = on_chunk_header;
    settings.on_chunk_complete = on_chunk_complete;

	message_begin = NULL;
	url = NULL;
	status = NULL;
	header_field = NULL;
	header_value = NULL;
	headers_complete = NULL;
	body = NULL;
	message_complete = NULL;
	chunk_header = NULL;
	chunk_complete = NULL;
}

MockParser::~MockParser()
{
	srs_freep(parser);

	srs_freep(message_begin);
	srs_freep(url);
	srs_freep(status);
	srs_freep(header_field);
	srs_freep(header_value);
	srs_freep(headers_complete);
	srs_freep(body);
	srs_freep(message_complete);
	srs_freep(chunk_header);
	srs_freep(chunk_complete);
}

int MockParser::on_message_begin(http_parser* parser)
{
    MockParser* obj = (MockParser*)parser->data;
    srs_assert(obj);

	srs_freep(obj->message_begin);
    obj->message_begin = new MockStage(parser);

    return 0;
}

int MockParser::on_url(http_parser* parser, const char* at, size_t length)
{
    MockParser* obj = (MockParser*)parser->data;
    srs_assert(obj);

	srs_freep(obj->url);
    obj->url = new MockStage(parser);
    obj->url->at = at;
    obj->url->length = length;

    return 0;
}

int MockParser::on_status(http_parser* parser, const char* at, size_t length)
{
    MockParser* obj = (MockParser*)parser->data;
    srs_assert(obj);

	srs_freep(obj->status);
    obj->status = new MockStage(parser);
    obj->status->at = at;
    obj->status->length = length;

    return 0;
}

int MockParser::on_header_field(http_parser* parser, const char* at, size_t length)
{
    MockParser* obj = (MockParser*)parser->data;
    srs_assert(obj);

	srs_freep(obj->header_field);
    obj->header_field = new MockStage(parser);
    obj->header_field->at = at;
    obj->header_field->length = length;

    return 0;
}

int MockParser::on_header_value(http_parser* parser, const char* at, size_t length)
{
    MockParser* obj = (MockParser*)parser->data;
    srs_assert(obj);

	srs_freep(obj->header_value);
    obj->header_value = new MockStage(parser);
    obj->header_value->at = at;
    obj->header_value->length = length;

    return 0;
}

int MockParser::on_headers_complete(http_parser* parser)
{
    MockParser* obj = (MockParser*)parser->data;
    srs_assert(obj);

	srs_freep(obj->headers_complete);
    obj->headers_complete = new MockStage(parser);

    return 0;
}

int MockParser::on_body(http_parser* parser, const char* at, size_t length)
{
    MockParser* obj = (MockParser*)parser->data;
    srs_assert(obj);

	srs_freep(obj->body);
    obj->body = new MockStage(parser);
    obj->body->at = at;
    obj->body->length = length;

    return 0;
}

int MockParser::on_message_complete(http_parser* parser)
{
    MockParser* obj = (MockParser*)parser->data;
    srs_assert(obj);

	srs_freep(obj->message_complete);
    obj->message_complete = new MockStage(parser);

    return 0;
}

int MockParser::on_chunk_header(http_parser* parser)
{
    MockParser* obj = (MockParser*)parser->data;
    srs_assert(obj);

	srs_freep(obj->chunk_header);
    obj->chunk_header = new MockStage(parser);

    return 0;
}

int MockParser::on_chunk_complete(http_parser* parser)
{
    MockParser* obj = (MockParser*)parser->data;
    srs_assert(obj);

	srs_freep(obj->chunk_complete);
    obj->chunk_complete = new MockStage(parser);

    return 0;
}

srs_error_t MockParser::parse(string data)
{
	srs_error_t err = srs_success;

	const char* buf = (const char*)data.data();
	size_t size = (size_t)data.length();
	size_t nparsed = http_parser_execute(parser, &settings, buf, size);
	parsed = nparsed;

	if (nparsed != size) {
		return srs_error_new(-1, "nparsed=%d, size=%d", nparsed, size);
	}

	return err;
}

VOID TEST(ProtocolHTTPTest, HTTPParser)
{
	srs_error_t err;

	if (true) {
		MockParser parser;
		// size = 70, nparsed = 70, nread = 0
		HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\n"));
		EXPECT_EQ(70, (int)parser.parsed);
		EXPECT_EQ(0, (int)parser.parser->nread);
		EXPECT_TRUE(!parser.body);
		EXPECT_TRUE(parser.headers_complete);
		EXPECT_TRUE(!parser.message_complete);
	}

	if (true) {
		MockParser parser;
		// size = 75, nparsed = 75, nread = 0
		HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\nHello"));
		EXPECT_EQ(75, (int)parser.parsed);
		EXPECT_EQ(0, (int)parser.parser->nread);
		EXPECT_TRUE(parser.body && 5 == parser.body->length);
		EXPECT_TRUE(parser.headers_complete);
		EXPECT_TRUE(parser.message_complete);
	}

	if (true) {
		MockParser parser;
		// size = 150, nparsed = 150, nread = 0
		HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\nHelloGET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\nWorld"));
		EXPECT_EQ(150, (int)parser.parsed);
		EXPECT_EQ(0, (int)parser.parser->nread);
	}

	if (true) {
		MockParser parser;
		// size = 70, nparsed = 70, nread = 0, content_length = 5, Header("Content-Length", 5)
		HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\n"));
		EXPECT_EQ(70, (int)parser.parsed);
		EXPECT_EQ(0, (int)parser.parser->nread);
		EXPECT_EQ(5, (int)parser.parser->content_length);

		// size = 79, nparsed = 5, nread = 1, content_length = -1, Header("Content-Length", 5)
		HELPER_EXPECT_FAILED(parser.parse("elloGET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\nHello"));
		EXPECT_EQ(5, (int)parser.parsed);
		EXPECT_EQ(1, (int)parser.parser->nread);
		EXPECT_EQ(-1, (int64_t)parser.parser->content_length);
	}

	if (true) {
		MockParser parser;
		// size = 70, nparsed = 70, nread = 0, content_length = 5, Header("Content-Length", 5)
		HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\n"));
		EXPECT_EQ(70, (int)parser.parsed);
		EXPECT_EQ(0, (int)parser.parser->nread);
		EXPECT_EQ(5, (int)parser.parser->content_length);

		// size = 80, nparsed = 70, nread = 0, content_length = 0, Header("Content-Length", 5)
		HELPER_EXPECT_SUCCESS(parser.parse("HelloGET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\nWorld"));
		EXPECT_EQ(80, (int)parser.parsed);
		EXPECT_EQ(0, (int)parser.parser->nread);
		EXPECT_EQ(0, (int)parser.parser->content_length);
	}

	if (true) {
		MockParser parser;
		// size = 73, nparsed = 73, nread = 0, content_length = 2, Header("Content-Length", 5)
		HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\nHel"));
		EXPECT_EQ(73, (int)parser.parsed);
		EXPECT_EQ(0, (int)parser.parser->nread);
		EXPECT_EQ(2, (int)parser.parser->content_length);
	}

	if (true) {
		MockParser parser;
		// size = 82, nparsed = 75, nread = 1, content_length = -1, Header("Content-Length", 5)
		HELPER_EXPECT_FAILED(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\nHello World!"));
		EXPECT_EQ(75, (int)parser.parsed);
		EXPECT_EQ(1, (int)parser.parser->nread);
		EXPECT_EQ(-1, (int64_t)parser.parser->content_length);
	}

	if (true) {
		MockParser parser;
		// size = 34, nparsed = 34, nread = 34
		HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHo"));
		EXPECT_EQ(34, (int)parser.parsed);
		EXPECT_EQ(34, (int)parser.parser->nread);

		// size = 41, nparsed = 41, nread = 0
		HELPER_EXPECT_SUCCESS(parser.parse("st: ossrs.net\r\nContent-Length: 5\r\n\r\nHello"));
		EXPECT_EQ(41, (int)parser.parsed);
		EXPECT_EQ(0, (int)parser.parser->nread);
	}

	if (true) {
		MockParser parser;
		// size = 41, nparsed = 41, nread = 41
		HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: oss"));
		EXPECT_EQ(41, (int)parser.parsed);
		EXPECT_EQ(41, (int)parser.parser->nread);

		// size = 34, nparsed = 34, nread = 0
		HELPER_EXPECT_SUCCESS(parser.parse("rs.net\r\nContent-Length: 5\r\n\r\nHello"));
		EXPECT_EQ(34, (int)parser.parsed);
		EXPECT_EQ(0, (int)parser.parser->nread);
	}

	if (true) {
		MockParser parser;
		// size = 48, nparsed = 48, nread = 48
		HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r"));
		EXPECT_EQ(48, (int)parser.parsed);
		EXPECT_EQ(48, (int)parser.parser->nread);

		// size = 27, nparsed = 27, nread = 0
		HELPER_EXPECT_SUCCESS(parser.parse("\nContent-Length: 5\r\n\r\nHello"));
		EXPECT_EQ(27, (int)parser.parsed);
		EXPECT_EQ(0, (int)parser.parser->nread);
	}

	if (true) {
		MockParser parser;
		// size = 68, nparsed = 68, nread = 68
		HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n"));
		EXPECT_EQ(68, (int)parser.parsed);
		EXPECT_EQ(68, (int)parser.parser->nread);

		// size = 7, nparsed = 7, nread = 0
		HELPER_EXPECT_SUCCESS(parser.parse("\r\nHello"));
		EXPECT_EQ(7, (int)parser.parsed);
		EXPECT_EQ(0, (int)parser.parser->nread);
	}

	if (true) {
		MockParser parser;
		// size = 69, nparsed = 69, nread = 69
		HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r"));
		EXPECT_EQ(69, (int)parser.parsed);
		EXPECT_EQ(69, (int)parser.parser->nread);

		// size = 6, nparsed = 6, nread = 0
		HELPER_EXPECT_SUCCESS(parser.parse("\nHello"));
		EXPECT_EQ(6, (int)parser.parsed);
		EXPECT_EQ(0, (int)parser.parser->nread);
	}

	if (true) {
		MockParser parser;
		// size = 75, nparsed = 75, nread = 0
		HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\nHello"));
		EXPECT_EQ(75, (int)parser.parsed);
		EXPECT_EQ(0, (int)parser.parser->nread);
	}

	if (true) {
		MockParser parser;
		// nparsed = 2, size = 2, nread = 2
		HELPER_EXPECT_SUCCESS(parser.parse("GE"));
		EXPECT_EQ(2, (int)parser.parsed);
		EXPECT_EQ(2, (int)parser.parser->nread);

		// size = 0, nparsed = 1, nread=2
		HELPER_EXPECT_FAILED(parser.parse(""));
		EXPECT_EQ(1, (int)parser.parsed);
		EXPECT_EQ(2, (int)parser.parser->nread);
	}

	if (true) {
		MockParser parser;
		// size = 2, nparsed = 2, nread = 2
		HELPER_EXPECT_SUCCESS(parser.parse("GE"));
		EXPECT_EQ(2, (int)parser.parsed);
		EXPECT_EQ(2, (int)parser.parser->nread);

		// size = 1, nparsed = 0, nread = 3
		HELPER_EXPECT_FAILED(parser.parse("X"));
		EXPECT_EQ(0, (int)parser.parsed);
		EXPECT_EQ(3, (int)parser.parser->nread);
	}

	if (true) {
		MockParser parser;
		// size = 2, nparsed = 2, nread = 2
		HELPER_EXPECT_SUCCESS(parser.parse("GE"));
		EXPECT_EQ(2, (int)parser.parsed);
		EXPECT_EQ(2, (int)parser.parser->nread);

		// size = 1, nparsed = 1, nread = 3
		HELPER_EXPECT_SUCCESS(parser.parse("T"));
		EXPECT_EQ(1, (int)parser.parsed);
		EXPECT_EQ(3, (int)parser.parser->nread);
	}

	if (true) {
		MockParser parser;
		// size = 3, nparsed = 3, nread = 3
		HELPER_EXPECT_SUCCESS(parser.parse("GET"));
		EXPECT_EQ(3, (int)parser.parsed);
		EXPECT_EQ(3, (int)parser.parser->nread);
	}
}

VOID TEST(ProtocolHTTPTest, ParseHTTPMessage)
{
    if (true) {
        MockBufferIO bio;
        SrsHttpParser hp;

        bio.append("GET /gslb/v1/versions HTTP/1.1\r\nContent-Length: 5\r\n\r\nHello");
        EXPECT_TRUE(0 == hp.initialize(HTTP_REQUEST, false));

        ISrsHttpMessage* req = NULL;
        ASSERT_TRUE(0 == hp.parse_message(&bio, &req));

        // We should read body, or next parsing message will fail.
        // @see https://github.com/ossrs/srs/issues/1181
        EXPECT_FALSE(req->body_reader()->eof());
        srs_freep(req);

		// Got new packet, notice that previous body still exists in bio.
        bio.append("GET /gslb/v2/versions HTTP/1.1\r\nContent-Length: 5\r\n\r\nHello");

        // Should fail because there is body which not read.
        // @see https://github.com/ossrs/srs/issues/1181
        ASSERT_FALSE(0 == hp.parse_message(&bio, &req));
        srs_freep(req);
    }

    if (true) {
        MockBufferIO bio;
        SrsHttpParser hp;

        bio.append("GET");
        EXPECT_TRUE(0 == hp.initialize(HTTP_REQUEST, false));

		// Should fail if not completed message.
        ISrsHttpMessage* req = NULL;
        ASSERT_FALSE(0 == hp.parse_message(&bio, &req));
        srs_freep(req);
    }
    
    if (true) {
        MockBufferIO bio;
        SrsHttpParser hp;
        
        bio.append("GET /gslb/v1/versions HTTP/1.1\r\nContent-Length: 5\r\n\r\nHello");
        ASSERT_TRUE(0 == hp.initialize(HTTP_REQUEST, false));
        
        ISrsHttpMessage* req = NULL;
        SrsAutoFree(ISrsHttpMessage, req);
        ASSERT_TRUE(0 == hp.parse_message(&bio, &req));
        
        char v[64] = {0};
        EXPECT_TRUE(0 == req->body_reader()->read(v, sizeof(v), NULL));
        EXPECT_TRUE(string("Hello") == string(v));
        
        EXPECT_TRUE(req->body_reader()->eof());
    }
    
    if (true) {
        MockBufferIO bio;
        SrsHttpParser hp;
        
        bio.append("GET /gslb/v1/versions HTTP/1.1\r\nContent-Length: 0\r\n\r\n");
        ASSERT_TRUE(0 == hp.initialize(HTTP_REQUEST, false));
        
        ISrsHttpMessage* req = NULL;
        SrsAutoFree(ISrsHttpMessage, req);
        EXPECT_TRUE(0 == hp.parse_message(&bio, &req));
    }
    
    if (true) {
        MockBufferIO bio;
        SrsHttpParser hp;
        
        bio.append("GET /gslb/v1/versions HTTP/1.1\r\n\r\n");
        ASSERT_TRUE(0 == hp.initialize(HTTP_REQUEST, false));
        
        ISrsHttpMessage* req = NULL;
        SrsAutoFree(ISrsHttpMessage, req);
        EXPECT_TRUE(0 == hp.parse_message(&bio, &req));
    }
    
    if (true) {
        MockBufferIO bio;
        SrsHttpParser hp;
        
        bio.append("GET /gslb/v1/versions HTTP/1.1\r\n\r\n");
        ASSERT_TRUE(0 == hp.initialize(HTTP_REQUEST, false));
        
        ISrsHttpMessage* req = NULL;
        SrsAutoFree(ISrsHttpMessage, req);
        EXPECT_TRUE(0 == hp.parse_message(&bio, &req));
    }
}

VOID TEST(ProtocolKbpsTest, Connections)
{
    if (true) {
        MockWallClock* clock = new MockWallClock();
        SrsAutoFree(MockWallClock, clock);
        MockStatistic* io = new MockStatistic();
        SrsAutoFree(MockStatistic, io);
        
        SrsKbps* kbps = new SrsKbps(clock->set_clock(0));
        SrsAutoFree(SrsKbps, kbps);
        kbps->set_io(io, io);
        
        // No data, 0kbps.
        kbps->sample();
        
        EXPECT_EQ(0, kbps->get_recv_kbps());
        EXPECT_EQ(0, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());
        
        EXPECT_EQ(0, kbps->get_send_kbps());
        EXPECT_EQ(0, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());
        
        // 800kbps in 30s.
        clock->set_clock(30 * 1000 * SRS_UTIME_MILLISECONDS);
        io->set_in(30 * 100 * 1000)->set_out(30 * 100 * 1000);
        kbps->sample();
        
        EXPECT_EQ(800, kbps->get_recv_kbps());
        EXPECT_EQ(800, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());
        
        EXPECT_EQ(800, kbps->get_send_kbps());
        EXPECT_EQ(800, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());
        
        // 800kbps in 300s.
        clock->set_clock(330 * 1000 * SRS_UTIME_MILLISECONDS);
        io->set_in(330 * 100 * 1000)->set_out(330 * 100 * 1000);
        kbps->sample();
        
        EXPECT_EQ(800, kbps->get_recv_kbps());
        EXPECT_EQ(800, kbps->get_recv_kbps_30s());
        EXPECT_EQ(800, kbps->get_recv_kbps_5m());
        
        EXPECT_EQ(800, kbps->get_send_kbps());
        EXPECT_EQ(800, kbps->get_send_kbps_30s());
        EXPECT_EQ(800, kbps->get_send_kbps_5m());
    }
    
    if (true) {
        MockWallClock* clock = new MockWallClock();
        SrsAutoFree(MockWallClock, clock);
        MockStatistic* io = new MockStatistic();
        SrsAutoFree(MockStatistic, io);
        
        SrsKbps* kbps = new SrsKbps(clock->set_clock(0));
        SrsAutoFree(SrsKbps, kbps);
        kbps->set_io(io, io);
        
        // No data, 0kbps.
        kbps->sample();
        
        EXPECT_EQ(0, kbps->get_recv_kbps());
        EXPECT_EQ(0, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());
        
        EXPECT_EQ(0, kbps->get_send_kbps());
        EXPECT_EQ(0, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());
        
        // 800kbps in 30s.
        clock->set_clock(30 * 1000 * SRS_UTIME_MILLISECONDS);
        io->set_in(30 * 100 * 1000);
        kbps->sample();
        
        EXPECT_EQ(800, kbps->get_recv_kbps());
        EXPECT_EQ(800, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());
        
        EXPECT_EQ(0, kbps->get_send_kbps());
        EXPECT_EQ(0, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());
        
        // 800kbps in 300s.
        clock->set_clock(330 * 1000 * SRS_UTIME_MILLISECONDS);
        io->set_in(330 * 100 * 1000);
        kbps->sample();
        
        EXPECT_EQ(800, kbps->get_recv_kbps());
        EXPECT_EQ(800, kbps->get_recv_kbps_30s());
        EXPECT_EQ(800, kbps->get_recv_kbps_5m());
        
        EXPECT_EQ(0, kbps->get_send_kbps());
        EXPECT_EQ(0, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());
    }
    
    if (true) {
        MockWallClock* clock = new MockWallClock();
        SrsAutoFree(MockWallClock, clock);
        MockStatistic* io = new MockStatistic();
        SrsAutoFree(MockStatistic, io);
        
        SrsKbps* kbps = new SrsKbps(clock->set_clock(0));
        SrsAutoFree(SrsKbps, kbps);
        kbps->set_io(io, io);
        
        // No data, 0kbps.
        kbps->sample();
        
        EXPECT_EQ(0, kbps->get_recv_kbps());
        EXPECT_EQ(0, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());
        
        EXPECT_EQ(0, kbps->get_send_kbps());
        EXPECT_EQ(0, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());
        
        // 800kbps in 30s.
        clock->set_clock(30 * 1000 * SRS_UTIME_MILLISECONDS);
        io->set_out(30 * 100 * 1000);
        kbps->sample();
        
        EXPECT_EQ(0, kbps->get_recv_kbps());
        EXPECT_EQ(0, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());
        
        EXPECT_EQ(800, kbps->get_send_kbps());
        EXPECT_EQ(800, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());
        
        // 800kbps in 300s.
        clock->set_clock(330 * 1000 * SRS_UTIME_MILLISECONDS);
        io->set_out(330 * 100 * 1000);
        kbps->sample();
        
        EXPECT_EQ(0, kbps->get_recv_kbps());
        EXPECT_EQ(0, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());
        
        EXPECT_EQ(800, kbps->get_send_kbps());
        EXPECT_EQ(800, kbps->get_send_kbps_30s());
        EXPECT_EQ(800, kbps->get_send_kbps_5m());
    }
}

VOID TEST(ProtocolKbpsTest, Delta)
{
    if (true) {
        MockWallClock* clock = new MockWallClock();
        SrsAutoFree(MockWallClock, clock);
        MockStatistic* io = new MockStatistic();
        SrsAutoFree(MockStatistic, io);
        
        SrsKbps* conn = new SrsKbps(clock->set_clock(0));
        SrsAutoFree(SrsKbps, conn);
        conn->set_io(io, io);
        
        // No data.
        ISrsKbpsDelta* delta = (ISrsKbpsDelta*)conn;
        int64_t in, out;
        delta->remark(&in, &out);
        EXPECT_EQ(0, in);
        EXPECT_EQ(0, out);
        
        // 800kb.
        io->set_in(100 * 1000)->set_out(100 * 1000);
        delta->remark(&in, &out);
        EXPECT_EQ(100 * 1000, in);
        EXPECT_EQ(100 * 1000, out);
        
        // No data.
        delta->remark(&in, &out);
        EXPECT_EQ(0, in);
        EXPECT_EQ(0, out);
    }
    
    if (true) {
        MockWallClock* clock = new MockWallClock();
        SrsAutoFree(MockWallClock, clock);
        MockStatistic* io = new MockStatistic();
        SrsAutoFree(MockStatistic, io);
        
        SrsKbps* conn = new SrsKbps(clock->set_clock(0));
        SrsAutoFree(SrsKbps, conn);
        conn->set_io(io, io);
        
        // No data.
        ISrsKbpsDelta* delta = (ISrsKbpsDelta*)conn;
        int64_t in, out;
        delta->remark(&in, &out);
        EXPECT_EQ(0, in);
        EXPECT_EQ(0, out);
        
        // 800kb.
        io->set_in(100 * 1000)->set_out(100 * 1000);
        delta->remark(&in, &out);
        EXPECT_EQ(100 * 1000, in);
        EXPECT_EQ(100 * 1000, out);
        
        // Kbps without io, gather delta.
        SrsKbps* kbps = new SrsKbps(clock->set_clock(0));
        SrsAutoFree(SrsKbps, kbps);
        kbps->set_io(NULL, NULL);
        
        // No data, 0kbps.
        kbps->sample();
        
        EXPECT_EQ(0, kbps->get_recv_kbps());
        EXPECT_EQ(0, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());
        
        EXPECT_EQ(0, kbps->get_send_kbps());
        EXPECT_EQ(0, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());
        
        // 800kbps in 30s.
        clock->set_clock(30 * 1000 * SRS_UTIME_MILLISECONDS);
        kbps->add_delta(30 * in, 30 * out);
        kbps->sample();
        
        EXPECT_EQ(800, kbps->get_recv_kbps());
        EXPECT_EQ(800, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());
        
        EXPECT_EQ(800, kbps->get_send_kbps());
        EXPECT_EQ(800, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());
    }
}

VOID TEST(ProtocolKbpsTest, RAWStatistic)
{
    if (true) {
        MockWallClock* clock = new MockWallClock();
        SrsAutoFree(MockWallClock, clock);
        MockStatistic* io = new MockStatistic();
        SrsAutoFree(MockStatistic, io);
        
        SrsKbps* conn = new SrsKbps(clock->set_clock(0));
        SrsAutoFree(SrsKbps, conn);
        conn->set_io(io, io);
        
        SrsKbps* kbps = new SrsKbps(clock->set_clock(0));
        SrsAutoFree(SrsKbps, kbps);
        kbps->set_io(conn, conn);
        
        // No data, 0kbps.
        kbps->sample();
        
        EXPECT_EQ(0, kbps->get_recv_kbps());
        EXPECT_EQ(0, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());
        
        EXPECT_EQ(0, kbps->get_send_kbps());
        EXPECT_EQ(0, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());
        
        // 800kbps in 30s.
        clock->set_clock(30 * 1000 * SRS_UTIME_MILLISECONDS);
        io->set_out(30 * 100 * 1000);
        kbps->sample();
        
        EXPECT_EQ(0, kbps->get_recv_kbps());
        EXPECT_EQ(0, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());
        
        EXPECT_EQ(800, kbps->get_send_kbps());
        EXPECT_EQ(800, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());
    }
    
    if (true) {
        MockWallClock* clock = new MockWallClock();
        SrsAutoFree(MockWallClock, clock);
        
        SrsKbps* kbps = new SrsKbps(clock->set_clock(0));
        SrsAutoFree(SrsKbps, kbps);
        
        // No io, no data.
        kbps->set_io(NULL, NULL);
        EXPECT_EQ(0, kbps->get_recv_bytes());
        EXPECT_EQ(0, kbps->get_send_bytes());
        
        // With io, zero data.
        MockStatistic* io = new MockStatistic();
        SrsAutoFree(MockStatistic, io);
        
        kbps->set_io(io, io);
        EXPECT_EQ(0, kbps->get_recv_bytes());
        EXPECT_EQ(0, kbps->get_send_bytes());
        
        // With io with data.
        io->set_in(100 * 1000)->set_out(100 * 1000);
        EXPECT_EQ(100 * 1000, kbps->get_recv_bytes());
        EXPECT_EQ(100 * 1000, kbps->get_send_bytes());
        
        // No io, cached data.
        kbps->set_io(NULL, NULL);
        EXPECT_EQ(100 * 1000, kbps->get_recv_bytes());
        EXPECT_EQ(100 * 1000, kbps->get_send_bytes());
        
        // Use the same IO, but as a fresh io.
        kbps->set_io(io, io);
        EXPECT_EQ(100 * 1000, kbps->get_recv_bytes());
        EXPECT_EQ(100 * 1000, kbps->get_send_bytes());
        
        io->set_in(150 * 1000)->set_out(150 * 1000);
        EXPECT_EQ(150 * 1000, kbps->get_recv_bytes());
        EXPECT_EQ(150 * 1000, kbps->get_send_bytes());
        
        // No io, cached data.
        kbps->set_io(NULL, NULL);
        EXPECT_EQ(150 * 1000, kbps->get_recv_bytes());
        EXPECT_EQ(150 * 1000, kbps->get_send_bytes());
    }
}

VOID TEST(ProtocolKbpsTest, WriteLargeIOVs)
{
    srs_error_t err;

    if (true) {
        iovec iovs[1];
        iovs[0].iov_base = (char*)"Hello";
        iovs[0].iov_len = 5;

        MockBufferIO io;
        ssize_t nn = 0;
        HELPER_EXPECT_SUCCESS(srs_write_large_iovs(&io, iovs, 1, &nn));
        EXPECT_EQ(5, nn);
        EXPECT_EQ(5, io.sbytes);
    }

    if (true) {
        iovec iovs[1024];
        int nn_iovs = (int)(sizeof(iovs)/sizeof(iovec));
        for (int i = 0; i < nn_iovs; i++) {
            iovs[i].iov_base = (char*)"Hello";
            iovs[i].iov_len = 5;
        }

        MockBufferIO io;
        ssize_t nn = 0;
        HELPER_EXPECT_SUCCESS(srs_write_large_iovs(&io, iovs, nn_iovs, &nn));
        EXPECT_EQ(5 * nn_iovs, nn);
        EXPECT_EQ(5 * nn_iovs, io.sbytes);
    }

    if (true) {
        iovec iovs[1025];
        int nn_iovs = (int)(sizeof(iovs)/sizeof(iovec));
        for (int i = 0; i < nn_iovs; i++) {
            iovs[i].iov_base = (char*)"Hello";
            iovs[i].iov_len = 5;
        }

        MockBufferIO io;
        ssize_t nn = 0;
        HELPER_EXPECT_SUCCESS(srs_write_large_iovs(&io, iovs, nn_iovs, &nn));
        EXPECT_EQ(5 * nn_iovs, nn);
        EXPECT_EQ(5 * nn_iovs, io.sbytes);
    }

    if (true) {
        iovec iovs[4096];
        int nn_iovs = (int)(sizeof(iovs)/sizeof(iovec));
        for (int i = 0; i < nn_iovs; i++) {
            iovs[i].iov_base = (char*)"Hello";
            iovs[i].iov_len = 5;
        }

        MockBufferIO io;
        ssize_t nn = 0;
        HELPER_EXPECT_SUCCESS(srs_write_large_iovs(&io, iovs, nn_iovs, &nn));
        EXPECT_EQ(5 * nn_iovs, nn);
        EXPECT_EQ(5 * nn_iovs, io.sbytes);
    }
}

